dcicutils 8.8.3.1b12__py3-none-any.whl → 8.8.3.1b14__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
dcicutils/data_readers.py CHANGED
@@ -66,6 +66,13 @@ class RowReader(abc.ABC):
66
66
  else:
67
67
  return value
68
68
 
69
+ @property
70
+ def nrows(self) -> int:
71
+ nrows = 0
72
+ for row in self:
73
+ nrows += 1
74
+ return nrows
75
+
69
76
  def open(self) -> None:
70
77
  pass
71
78
 
@@ -192,6 +199,10 @@ class Excel:
192
199
  return True
193
200
  return False
194
201
 
202
+ @property
203
+ def nsheets(self) -> int:
204
+ return len(self.sheet_names)
205
+
195
206
  def __del__(self) -> None:
196
207
  if (workbook := self._workbook) is not None:
197
208
  self._workbook = None
@@ -1,6 +1,7 @@
1
1
  from dcicutils.misc_utils import normalize_spaces
2
2
  from datetime import datetime, timedelta, timezone
3
- from typing import Optional, Tuple
3
+ from dateutil import parser as datetime_parser
4
+ from typing import Optional, Tuple, Union
4
5
 
5
6
 
6
7
  def parse_datetime_string(value: str) -> Optional[datetime]:
@@ -82,6 +83,32 @@ def normalize_date_string(value: str) -> Optional[str]:
82
83
  return d.strftime("%Y-%m-%d") if d else None
83
84
 
84
85
 
86
+ def get_timezone(hours: int, minutes: Optional[int] = None) -> timezone:
87
+ try:
88
+ return timezone(timedelta(hours=hours, minutes=minutes or 0))
89
+ except Exception:
90
+ return timezone.utc
91
+
92
+
93
+ def get_timezone_hours_minutes(tz: timezone) -> Tuple[int, int]:
94
+ """
95
+ Returns a tuple with the integer hours and minutes offset for the given timezone.
96
+ """
97
+ tz_minutes = datetime.now(tz).utcoffset().total_seconds() / 60
98
+ return int(tz_minutes // 60), int(abs(tz_minutes % 60))
99
+
100
+
101
+ def get_utc_timezone() -> timezone:
102
+ return timezone.utc
103
+
104
+
105
+ def get_local_timezone() -> timezone:
106
+ """
107
+ Returns current/local timezone as a datetime.timezone object.
108
+ """
109
+ return datetime.now().astimezone().tzinfo
110
+
111
+
85
112
  def get_local_timezone_string() -> str:
86
113
  """
87
114
  Returns current/local timezone in format like: "-05:00".
@@ -96,3 +123,91 @@ def get_local_timezone_hours_minutes() -> Tuple[int, int]:
96
123
  """
97
124
  tz_minutes = datetime.now(timezone.utc).astimezone().utcoffset().total_seconds() / 60
98
125
  return int(tz_minutes // 60), int(abs(tz_minutes % 60))
126
+
127
+
128
+ def parse_datetime(value: str, utc: bool = False, tz: Optional[timezone] = None) -> Optional[datetime]:
129
+ """
130
+ Parses the given string into a datetime, if possible, and returns that value,
131
+ or None if not able to parse. The timezone of the returned datetime will be the
132
+ local timezone; or if the given utc argument is True then it will be UTC; or if the
133
+ given tz argument is a datetime.timezone then return datetime will be in that timezone.
134
+ """
135
+ if isinstance(value, datetime):
136
+ return value
137
+ elif not isinstance(value, str):
138
+ return None
139
+ try:
140
+ # This dateutil.parser handles quite a wide variety of formats and suits our needs.
141
+ value = datetime_parser.parse(value)
142
+ if utc is True:
143
+ # If the given utc argument is True then it trumps any tz argument if given.
144
+ tz = timezone.utc
145
+ if value.tzinfo is not None:
146
+ # The given value had an explicit timezone specified.
147
+ if isinstance(tz, timezone):
148
+ return value.astimezone(tz)
149
+ return value
150
+ return value.replace(tzinfo=tz if isinstance(tz, timezone) else get_local_timezone())
151
+ except Exception:
152
+ return None
153
+
154
+
155
+ def format_datetime(value: datetime,
156
+ utc: bool = False,
157
+ iso: bool = False,
158
+ ms: bool = False,
159
+ tz: Optional[Union[timezone, bool]] = None,
160
+ notz: bool = False,
161
+ noseconds: bool = False,
162
+ verbose: bool = False,
163
+ noseparator: bool = False,
164
+ noday: bool = False) -> Optional[str]:
165
+ """
166
+ Returns the given datetime as a string in "YYYY:MM:DD hh:mm:ss tz" format, for
167
+ example "2024-04-17 15:42:26 EDT". If the given notz argument is True then omits
168
+ the timezone; if the noseconds argument is given the omits the seconds. If the given
169
+ verbose argument is True then returns a really verbose version of the datetime, for
170
+ example "Wednesday, April 17, 2024 | 15:42:26 EDT"; if the noseparator argument is
171
+ True then omits the "|" separator; if the noday argument is True then omits the day
172
+ of week part. The timezone of the returned datetime string will default to the local
173
+ one; if the given utc argument is True then it will be UTC; or if the given tz
174
+ argument is a datetime.timezone it will be in that timezone.
175
+ """
176
+ if not isinstance(value, datetime):
177
+ if not isinstance(value, str) or not (value := parse_datetime(value)):
178
+ return None
179
+ try:
180
+ if utc is True:
181
+ tz = timezone.utc
182
+ elif not isinstance(tz, timezone):
183
+ tz = get_local_timezone()
184
+ if tz is True:
185
+ notz = False
186
+ elif tz is False:
187
+ notz = True
188
+ if noseconds is True:
189
+ ms = False
190
+ value = value.astimezone(tz)
191
+ if iso:
192
+ if notz is True:
193
+ value = value.replace(tzinfo=None)
194
+ if not (ms is True):
195
+ value = value.replace(microsecond=0)
196
+ if noseconds is True:
197
+ if notz is True:
198
+ return value.strftime(f"%Y-%m-%dT%H:%M")
199
+ tz = value.strftime("%z")
200
+ tz = tz[:3] + ":" + tz[3:]
201
+ return value.strftime(f"%Y-%m-%dT%H:%M") + tz
202
+ return value.isoformat()
203
+ if verbose:
204
+ return value.strftime(
205
+ f"{'' if noday is True else '%A, '}%B %-d, %Y{'' if noseparator is True else ' |'}"
206
+ f" %-I:%M{'' if noseconds is True else ':%S'}"
207
+ f"{f'.%f' if ms is True else ''} %p{'' if notz is True else ' %Z'}")
208
+ else:
209
+ return value.strftime(
210
+ f"%Y-%m-%d %H:%M{'' if noseconds is True else ':%S'}"
211
+ f"{f'.%f' if ms is True else ''}{'' if notz is True else ' %Z'}")
212
+ except Exception:
213
+ return None
dcicutils/progress_bar.py CHANGED
@@ -50,6 +50,7 @@ class ProgressBar:
50
50
  def __init__(self, total: Optional[int] = None,
51
51
  description: Optional[str] = None,
52
52
  use_byte_size_for_rate: bool = False,
53
+ use_ascii: bool = False,
53
54
  catch_interrupt: bool = True,
54
55
  interrupt: Optional[Callable] = None,
55
56
  interrupt_continue: Optional[Callable] = None,
@@ -66,6 +67,7 @@ class ProgressBar:
66
67
  self._tidy_output_hack = (tidy_output_hack is True)
67
68
  self._stop_requested = False
68
69
  self._use_byte_size_for_rate = (use_byte_size_for_rate is True and self._tidy_output_hack)
70
+ self._use_ascii = (use_ascii is True)
69
71
  # Interrupt handling. We do not do the actual (signal) interrupt setup
70
72
  # in self._initialize as that could be called from a (sub) thread; and in
71
73
  # Python we can only set a signal (SIGINT in our case) on the main thread.
@@ -103,7 +105,7 @@ class ProgressBar:
103
105
  else:
104
106
  bar_format = "{l_bar}{bar}| {n_fmt}/{total_fmt} | {rate_fmt} | {elapsed}{postfix} | ETA: {remaining} "
105
107
  self._bar = TQDM(total=self._total, desc=self._description,
106
- dynamic_ncols=True, bar_format=bar_format, unit="", file=sys.stdout)
108
+ dynamic_ncols=True, bar_format=bar_format, unit="", file=sys.stdout, ascii=self._use_ascii)
107
109
  self._started = time.time()
108
110
  if self._disabled:
109
111
  self._bar.disable = True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dcicutils
3
- Version: 8.8.3.1b12
3
+ Version: 8.8.3.1b14
4
4
  Summary: Utility package for interacting with the 4DN Data Portal and other 4DN resources
5
5
  Home-page: https://github.com/4dn-dcic/utils
6
6
  License: MIT
@@ -10,9 +10,9 @@ dcicutils/common.py,sha256=YE8Mt5-vaZWWz4uaChSVhqGFbFtW5QKtnIyOr4zG4vM,3955
10
10
  dcicutils/contribution_scripts.py,sha256=0k5Gw1TumcD5SAcXVkDd6-yvuMEw-jUp5Kfb7FJH6XQ,2015
11
11
  dcicutils/contribution_utils.py,sha256=vYLS1JUB3sKd24BUxZ29qUBqYeQBLK9cwo8x3k64uPg,25653
12
12
  dcicutils/creds_utils.py,sha256=xrLekD49Ex0GOpL9n7LlJA4gvNcY7txTVFOSYD7LvEU,11113
13
- dcicutils/data_readers.py,sha256=WWH_VDz2KnNv_FoTjfFwrg6zh9asl8Q-uEV2V3XuyUg,7414
13
+ dcicutils/data_readers.py,sha256=6EMrY7TjDE8H7bA_TCWtpLQP7slJ0YTL77_dNh6e7sg,7626
14
14
  dcicutils/data_utils.py,sha256=k2OxOlsx7AJ6jF-YNlMyGus_JqSUBe4_n1s65Mv1gQQ,3098
15
- dcicutils/datetime_utils.py,sha256=EODDGAngp1yh2ZlDIuI7tB74JBJucw2DljqfPknzK0Y,4666
15
+ dcicutils/datetime_utils.py,sha256=S3SCmpqv_Z0CS8_yqZsoN4aL1Epu8jwKs6DDYs96OdM,9427
16
16
  dcicutils/deployment_utils.py,sha256=sKv8Jb-_Zw2aH3OAywRlsre-Cqm3a7fEGG3_1PT-r-s,69908
17
17
  dcicutils/diff_utils.py,sha256=sQx-yz56DHAcQWOChYbAG3clXu7TbiZKlw-GggeveO0,8118
18
18
  dcicutils/docker_utils.py,sha256=30gUiqz7X9rJwSPXTPn4ewjQibUgoSJqhP9o9vn5X-A,1747
@@ -48,7 +48,7 @@ dcicutils/obfuscation_utils.py,sha256=fo2jOmDRC6xWpYX49u80bVNisqRRoPskFNX3ymFAmj
48
48
  dcicutils/opensearch_utils.py,sha256=V2exmFYW8Xl2_pGFixF4I2Cc549Opwe4PhFi5twC0M8,1017
49
49
  dcicutils/portal_object_utils.py,sha256=gDXRgPsRvqCFwbC8WatsuflAxNiigOnqr0Hi93k3AgE,15422
50
50
  dcicutils/portal_utils.py,sha256=DYyE5o15GekDgzpJWas9iS7klAYbjJZUPW0G42McArk,30779
51
- dcicutils/progress_bar.py,sha256=l9h8o-RVvjGTt0J5qnxhMq7BaatJn1SnfOpWxHC2hL0,17278
51
+ dcicutils/progress_bar.py,sha256=th-M7Q1UfkmQWc8M36lfVoRhFAQVRT9fSpdHi5RHqNY,17389
52
52
  dcicutils/project_utils.py,sha256=qPdCaFmWUVBJw4rw342iUytwdQC0P-XKpK4mhyIulMM,31250
53
53
  dcicutils/qa_checkers.py,sha256=cdXjeL0jCDFDLT8VR8Px78aS10hwNISOO5G_Zv2TZ6M,20534
54
54
  dcicutils/qa_utils.py,sha256=TT0SiJWiuxYvbsIyhK9VO4uV_suxhB6CpuC4qPacCzQ,160208
@@ -72,8 +72,8 @@ dcicutils/trace_utils.py,sha256=g8kwV4ebEy5kXW6oOrEAUsurBcCROvwtZqz9fczsGRE,1769
72
72
  dcicutils/validation_utils.py,sha256=cMZIU2cY98FYtzK52z5WUYck7urH6JcqOuz9jkXpqzg,14797
73
73
  dcicutils/variant_utils.py,sha256=2H9azNx3xAj-MySg-uZ2SFqbWs4kZvf61JnK6b-h4Qw,4343
74
74
  dcicutils/zip_utils.py,sha256=rnjNv_k6L9jT2SjDSgVXp4BEJYLtz9XN6Cl2Fy-tqnM,2027
75
- dcicutils-8.8.3.1b12.dist-info/LICENSE.txt,sha256=qnwSmfnEWMl5l78VPDEzAmEbLVrRqQvfUQiHT0ehrOo,1102
76
- dcicutils-8.8.3.1b12.dist-info/METADATA,sha256=U0KtU7pIabtImsxBvw3OHYkEp76VaOkBy44A0NApfEw,3357
77
- dcicutils-8.8.3.1b12.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
78
- dcicutils-8.8.3.1b12.dist-info/entry_points.txt,sha256=51Q4F_2V10L0282W7HFjP4jdzW4K8lnWDARJQVFy_hw,270
79
- dcicutils-8.8.3.1b12.dist-info/RECORD,,
75
+ dcicutils-8.8.3.1b14.dist-info/LICENSE.txt,sha256=qnwSmfnEWMl5l78VPDEzAmEbLVrRqQvfUQiHT0ehrOo,1102
76
+ dcicutils-8.8.3.1b14.dist-info/METADATA,sha256=Od4jg67J1tt_7cxv1n04p8PGmpjbnlfi9-EKsMS-GKk,3357
77
+ dcicutils-8.8.3.1b14.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
78
+ dcicutils-8.8.3.1b14.dist-info/entry_points.txt,sha256=51Q4F_2V10L0282W7HFjP4jdzW4K8lnWDARJQVFy_hw,270
79
+ dcicutils-8.8.3.1b14.dist-info/RECORD,,