def validate(self, *, tags: List[str], project: Optional[str]) -> None: for tag in tags: if tag not in self._tags_by_name: raise utils.UsageError(f"Unknown tag {tag}") if project is not None and project not in self._projects_by_name: raise utils.UsageError( f"Unknown project {project}\n" f"Available projects: " f"[yellow]{', '.join(self._projects_by_name)}[/yellow]")
def _parse_time(self, time_str: str) -> datetime.time: if time_str == "now": now = datetime.datetime.now() if self.date != now.date(): raise utils.UsageError( "Can't combine 'now' with different date") return now.time() elif time_str == "/": return None try: return datetime.datetime.strptime(time_str, "%H:%M").time() except ValueError as e: raise utils.UsageError(str(e))
def _parse_date(self, arg: str) -> None: if self.date != datetime.datetime.now().date(): raise utils.UsageError("Multiple dates") midnight = datetime.datetime.combine(datetime.datetime.now(), datetime.time()) parsed = dateparser.parse(arg, settings={"RELATIVE_BASE": midnight}) if parsed is None: raise utils.UsageError(f"Couldn't parse date {arg}") if parsed.time() != datetime.time(): raise utils.UsageError(f"Date {arg} contains unexpected time") self.date = parsed.date()
def _parse_timespan(self, arg: str) -> None: try: start_str, end_str = arg.split("-") except ValueError: raise utils.UsageError( f"Couldn't parse timespan {arg} (too many '-')") start_time = self._parse_time(start_str) end_time = self._parse_time(end_str) if start_time is None and end_time is None: raise utils.UsageError( "Either start or end time needs to be given") self._timespans.append((start_time, end_time))
def _fetch_workspace_id(self) -> None: assert self.workspace_name is not None workspaces = self._api_get("workspaces") for workspace in workspaces: if workspace["name"] == self.workspace_name: self._workspace_id = workspace["id"] return names = [workspace["name"] for workspace in workspaces] raise utils.UsageError( f"No workspace [yellow]{self.workspace_name}[/yellow] found!\n" f"Available workspaces: [yellow]{', '.join(names)}[/yellow]")
def __init__(self, debug: bool = False, workspace: str = None) -> None: self._debug = debug try: key = os.environ["CLOCKIFY_API_KEY"] except KeyError as e: raise utils.UsageError(f"{e} not defined in environment") if workspace is None: try: self.workspace_name = os.environ["CLOCKIFY_WORKSPACE"] except KeyError as e: self.workspace_name = None else: self.workspace_name = workspace self._headers = {"X-Api-Key": key} self._user_id = None self._workspace_id = None self._projects_by_name: Dict[str, str] = {} self._projects_by_id: Dict[str, str] = {} self._tags_by_name: Dict[str, str] = {} self._tags_by_id: Dict[str, str] = {}
def parse(self, args: List[str] = None) -> None: parsed = self._parser.parse_args(args) self.debug = parsed.debug self.pager = not parsed.no_pager self.conky = parsed.conky self.conky_error_color = parsed.conky_error_color time_pattern = r"(\d\d?:\d\d?|/|now)" timespan_re = re.compile(f"{time_pattern}-{time_pattern}") for arg in parsed.inputs: if timespan_re.fullmatch(arg): self._parse_timespan(arg) elif arg[0] == "+": self._parse_tag(arg[1:]) elif arg[0] == "@": self._parse_project(arg[1:]) elif arg[0] == "$": self._parse_billable(arg[1:]) elif arg[0] == ".": self._parse_date(arg[1:]) elif arg[0] == "^": self._parse_workspace(arg[1:]) elif arg == "start": self._parse_timespan("now-/") elif arg == "stop": self._parse_timespan("/-now") else: self._parse_description(arg) self.entries = [ client.Entry( start=self._combine_date(start_time), end=self._combine_date(end_time), description=self._description, billable=self._billable, project=self.project, ) for (start_time, end_time) in self._timespans ] if parsed.dump: try: self.dump = datetime.datetime.strptime(parsed.dump, "%Y-%m") except ValueError: raise utils.UsageError( f"Unparseable month {parsed.dump} (use YYYY-MM)") has_new_entries = any(entry.start is not None for entry in self.entries) if not has_new_entries: if self._description: raise utils.UsageError( f"Description {self._description} given without new entries" ) elif self._billable: raise utils.UsageError("Billable given without new entries") elif self.project: raise utils.UsageError( f"Project {self.project} given without new entries") elif self.tags: raise utils.UsageError( f"Tags {self.tags} given without new entries") if parsed.dump and self.date != datetime.datetime.now().date(): raise utils.UsageError(f"Date {self.date} given with --dump")
def _parse_billable(self, arg: str) -> None: if arg: raise utils.UsageError(f"Invalid billable arg {arg}") self._billable = True
def _parse_workspace(self, arg: str) -> None: if self.workspace is not None: raise utils.UsageError( f"Multiple workspaces: {self.workspace}, {arg}") self.workspace = arg