Repository: frictionlessdata/frictionless-py

Issue:
Describe should provide bytes and hash for excel files
# Overview

```
$ frictionless describe file.xlsx --stats
stats:
  bytes: 0
  fields: 38
  hash: ''
  rows: 35000
```


Discussion:

Patch:
diff --git a/frictionless/plugins/excel.py b/frictionless/plugins/excel.py
index dd691e63..0e6329ad 100644
--- a/frictionless/plugins/excel.py
+++ b/frictionless/plugins/excel.py
@@ -2,6 +2,7 @@ import os
 import sys
 import shutil
 import atexit
+import hashlib
 import tempfile
 import datetime
 from itertools import chain
@@ -253,6 +254,23 @@ class XlsxParser(Parser):
                 cells, dialect.preserve_formatting, dialect.adjust_floating_point_error
             )
 
+        # Calculate stats
+        # TODO: remove when the proper implementation is in-place:
+        # https://github.com/frictionlessdata/frictionless-py/issues/438
+        if self.resource.scheme == "file":
+            stat = os.stat(self.resource.fullpath)
+            self.resource.stats["bytes"] = stat.st_size
+            if self.resource.hashing:
+                try:
+                    hasher = hashlib.new(self.resource.hashing)
+                    with open(self.resource.fullpath, "rb") as file:
+                        for chunk in iter(lambda: file.read(4096), b""):
+                            hasher.update(chunk)
+                        self.resource.stats["hash"] = hasher.hexdigest()
+                except Exception as exception:
+                    error = errors.HashingError(note=str(exception))
+                    raise FrictionlessException(error)
+
     # Write
 
     def write_row_stream(self, resource):


Test patch:
diff --git a/tests/plugins/test_excel.py b/tests/plugins/test_excel.py
index 59a5fbb6..6e3e1927 100644
--- a/tests/plugins/test_excel.py
+++ b/tests/plugins/test_excel.py
@@ -371,3 +371,10 @@ def test_xlsx_parser_multiline_header_with_merged_cells_issue_1024():
     with Resource("data/issue-1024.xlsx", dialect=dialect, layout=layout) as resource:
         assert resource.header
         assert resource.header[21] == "Current Phase P3+ #"
+
+
+def test_xlsx_parser_stats_no_bytes_and_hash_issue_938():
+    resource = Resource("data/table.xlsx")
+    resource.infer(stats=True)
+    assert resource.stats["hash"] == "dddeae0cad9b46e8d7a2d157774dcf5b"
+    assert resource.stats["bytes"] == 6230