diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 000000000..b299125bd
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,28 @@
+# Use the latest 2.1 version of CircleCI pipeline process engine.
+# See: https://circleci.com/docs/configuration-reference
+version: 2.1
+
+# Define a job to be invoked later in a workflow.
+# See: https://circleci.com/docs/configuration-reference/#jobs
+jobs:
+ say-hello:
+ # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub.
+ # See: https://circleci.com/docs/configuration-reference/#executor-job
+ docker:
+ - image: cimg/base:stable
+ # Add steps to the job
+ # See: https://circleci.com/docs/configuration-reference/#steps
+ steps:
+ - checkout
+ - run:
+ name: "Say hello"
+ command: "echo Hello, World!"
+
+orbs:
+ codecov: codecov/codecov@3.2.4
+# Orchestrate jobs using workflows
+# See: https://circleci.com/docs/configuration-reference/#workflows
+workflows:
+ say-hello-workflow:
+ jobs:
+ - say-hello
diff --git a/.pylintrc b/.pylintrc
new file mode 100644
index 000000000..07c0e858e
--- /dev/null
+++ b/.pylintrc
@@ -0,0 +1,634 @@
+[MAIN]
+
+# Analyse import fallback blocks. This can be used to support both Python 2 and
+# 3 compatible code, which means that the block might have code that exists
+# only in one or another interpreter, leading to false positives when analysed.
+analyse-fallback-blocks=no
+
+# Clear in-memory caches upon conclusion of linting. Useful if running pylint
+# in a server-like mode.
+clear-cache-post-run=no
+
+# Load and enable all available extensions. Use --list-extensions to see a list
+# all available extensions.
+#enable-all-extensions=
+
+# In error mode, messages with a category besides ERROR or FATAL are
+# suppressed, and no reports are done by default. Error mode is compatible with
+# disabling specific errors.
+#errors-only=
+
+# Always return a 0 (non-error) status code, even if lint errors are found.
+# This is primarily useful in continuous integration scripts.
+#exit-zero=
+
+# A comma-separated list of package or module names from where C extensions may
+# be loaded. Extensions are loading into the active Python interpreter and may
+# run arbitrary code.
+extension-pkg-allow-list=
+
+# A comma-separated list of package or module names from where C extensions may
+# be loaded. Extensions are loading into the active Python interpreter and may
+# run arbitrary code. (This is an alternative name to extension-pkg-allow-list
+# for backward compatibility.)
+extension-pkg-whitelist=
+
+# Return non-zero exit code if any of these messages/categories are detected,
+# even if score is above --fail-under value. Syntax same as enable. Messages
+# specified are enabled, while categories only check already-enabled messages.
+fail-on=
+
+# Specify a score threshold under which the program will exit with error.
+fail-under=10
+
+# Interpret the stdin as a python script, whose filename needs to be passed as
+# the module_or_package argument.
+#from-stdin=
+
+# Files or directories to be skipped. They should be base names, not paths.
+ignore=CVS
+
+# Add files or directories matching the regular expressions patterns to the
+# ignore-list. The regex matches against paths and can be in Posix or Windows
+# format. Because '\\' represents the directory delimiter on Windows systems,
+# it can't be used as an escape character.
+ignore-paths=
+
+# Files or directories matching the regular expression patterns are skipped.
+# The regex matches against base names, not paths. The default value ignores
+# Emacs file locks
+ignore-patterns=^\.#
+
+# List of module names for which member attributes should not be checked
+# (useful for modules/projects where namespaces are manipulated during runtime
+# and thus existing member attributes cannot be deduced by static analysis). It
+# supports qualified module names, as well as Unix pattern matching.
+ignored-modules=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
+# number of processors available to use, and will cap the count on Windows to
+# avoid hangs.
+jobs=1
+
+# Control the amount of potential inferred values when inferring a single
+# object. This can help the performance when dealing with large functions or
+# complex, nested conditions.
+limit-inference-results=100
+
+# List of plugins (as comma separated values of python module names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# Minimum Python version to use for version dependent checks. Will default to
+# the version used to run pylint.
+py-version=3.11
+
+# Discover python modules and packages in the file system subtree.
+recursive=no
+
+# Add paths to the list of the source roots. Supports globbing patterns. The
+# source root is an absolute path or a path relative to the current working
+# directory used to determine a package namespace for modules located under the
+# source root.
+source-roots=
+
+# When enabled, pylint would attempt to guess common misconfiguration and emit
+# user-friendly hints instead of false-positive error messages.
+suggestion-mode=yes
+
+# Allow loading of arbitrary C extensions. Extensions are imported into the
+# active Python interpreter and may run arbitrary code.
+unsafe-load-any-extension=no
+
+# In verbose mode, extra non-checker-related info will be displayed.
+#verbose=
+
+
+[BASIC]
+
+# Naming style matching correct argument names.
+argument-naming-style=snake_case
+
+# Regular expression matching correct argument names. Overrides argument-
+# naming-style. If left empty, argument names will be checked with the set
+# naming style.
+#argument-rgx=
+
+# Naming style matching correct attribute names.
+attr-naming-style=snake_case
+
+# Regular expression matching correct attribute names. Overrides attr-naming-
+# style. If left empty, attribute names will be checked with the set naming
+# style.
+#attr-rgx=
+
+# Bad variable names which should always be refused, separated by a comma.
+bad-names=foo,
+ bar,
+ baz,
+ toto,
+ tutu,
+ tata
+
+# Bad variable names regexes, separated by a comma. If names match any regex,
+# they will always be refused
+bad-names-rgxs=
+
+# Naming style matching correct class attribute names.
+class-attribute-naming-style=any
+
+# Regular expression matching correct class attribute names. Overrides class-
+# attribute-naming-style. If left empty, class attribute names will be checked
+# with the set naming style.
+#class-attribute-rgx=
+
+# Naming style matching correct class constant names.
+class-const-naming-style=UPPER_CASE
+
+# Regular expression matching correct class constant names. Overrides class-
+# const-naming-style. If left empty, class constant names will be checked with
+# the set naming style.
+#class-const-rgx=
+
+# Naming style matching correct class names.
+class-naming-style=PascalCase
+
+# Regular expression matching correct class names. Overrides class-naming-
+# style. If left empty, class names will be checked with the set naming style.
+#class-rgx=
+
+# Naming style matching correct constant names.
+const-naming-style=UPPER_CASE
+
+# Regular expression matching correct constant names. Overrides const-naming-
+# style. If left empty, constant names will be checked with the set naming
+# style.
+#const-rgx=
+
+# Minimum line length for functions/classes that require docstrings, shorter
+# ones are exempt.
+docstring-min-length=-1
+
+# Naming style matching correct function names.
+function-naming-style=snake_case
+
+# Regular expression matching correct function names. Overrides function-
+# naming-style. If left empty, function names will be checked with the set
+# naming style.
+#function-rgx=
+
+# Good variable names which should always be accepted, separated by a comma.
+good-names=i,
+ j,
+ k,
+ ex,
+ Run,
+ _
+
+# Good variable names regexes, separated by a comma. If names match any regex,
+# they will always be accepted
+good-names-rgxs=
+
+# Include a hint for the correct naming format with invalid-name.
+include-naming-hint=no
+
+# Naming style matching correct inline iteration names.
+inlinevar-naming-style=any
+
+# Regular expression matching correct inline iteration names. Overrides
+# inlinevar-naming-style. If left empty, inline iteration names will be checked
+# with the set naming style.
+#inlinevar-rgx=
+
+# Naming style matching correct method names.
+method-naming-style=snake_case
+
+# Regular expression matching correct method names. Overrides method-naming-
+# style. If left empty, method names will be checked with the set naming style.
+#method-rgx=
+
+# Naming style matching correct module names.
+module-naming-style=snake_case
+
+# Regular expression matching correct module names. Overrides module-naming-
+# style. If left empty, module names will be checked with the set naming style.
+#module-rgx=
+
+# Colon-delimited sets of names that determine each other's naming style when
+# the name regexes allow several styles.
+name-group=
+
+# Regular expression which should only match function or class names that do
+# not require a docstring.
+no-docstring-rgx=^_
+
+# List of decorators that produce properties, such as abc.abstractproperty. Add
+# to this list to register other decorators that produce valid properties.
+# These decorators are taken in consideration only for invalid-name.
+property-classes=abc.abstractproperty
+
+# Regular expression matching correct type alias names. If left empty, type
+# alias names will be checked with the set naming style.
+#typealias-rgx=
+
+# Regular expression matching correct type variable names. If left empty, type
+# variable names will be checked with the set naming style.
+#typevar-rgx=
+
+# Naming style matching correct variable names.
+variable-naming-style=snake_case
+
+# Regular expression matching correct variable names. Overrides variable-
+# naming-style. If left empty, variable names will be checked with the set
+# naming style.
+#variable-rgx=
+
+
+[CLASSES]
+
+# Warn about protected attribute access inside special methods
+check-protected-access-in-special-methods=no
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,
+ __new__,
+ setUp,
+ asyncSetUp,
+ __post_init__
+
+# List of member names, which should be excluded from the protected access
+# warning.
+exclude-protected=_asdict,_fields,_replace,_source,_make,os._exit
+
+# List of valid names for the first argument in a class method.
+valid-classmethod-first-arg=cls
+
+# List of valid names for the first argument in a metaclass class method.
+valid-metaclass-classmethod-first-arg=mcs
+
+
+[DESIGN]
+
+# List of regular expressions of class ancestor names to ignore when counting
+# public methods (see R0903)
+exclude-too-few-public-methods=
+
+# List of qualified class names to ignore when counting class parents (see
+# R0901)
+ignored-parents=
+
+# Maximum number of arguments for function / method.
+max-args=7
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=10
+
+# Maximum number of boolean expressions in an if statement (see R0916).
+max-bool-expr=10
+
+# Maximum number of branch for function / method body.
+max-branches=20
+
+# Maximum number of locals for function / method body.
+max-locals=20
+
+# Maximum number of parents for a class (see R0901).
+max-parents=10
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+# Maximum number of return / yield for function / method body.
+max-returns=10
+
+# Maximum number of statements in function / method body.
+max-statements=150
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=2
+
+
+[EXCEPTIONS]
+
+# Exceptions that will emit a warning when caught.
+overgeneral-exceptions=builtins.BaseException,builtins.Exception
+
+
+[FORMAT]
+
+# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
+expected-line-ending-format=
+
+# Regexp for a line that is allowed to be longer than the limit.
+ignore-long-lines=^\s*(# )??$
+
+# Number of spaces of indent required inside a hanging or continued line.
+indent-after-paren=4
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string=' '
+
+# Maximum number of characters on a single line.
+max-line-length=200
+
+# Maximum number of lines in a module.
+max-module-lines=1000
+
+# Allow the body of a class to be on the same line as the declaration if body
+# contains single statement.
+single-line-class-stmt=no
+
+# Allow the body of an if to be on the same line as the test if there is no
+# else.
+single-line-if-stmt=no
+
+
+[IMPORTS]
+
+# List of modules that can be imported at any level, not just the top level
+# one.
+allow-any-import-level=
+
+# Allow explicit reexports by alias from a package __init__.
+allow-reexport-from-package=no
+
+# Allow wildcard imports from modules that define __all__.
+allow-wildcard-with-all=no
+
+# Deprecated modules which should not be used, separated by a comma.
+deprecated-modules=
+
+# Output a graph (.gv or any supported image format) of external dependencies
+# to the given file (report RP0402 must not be disabled).
+ext-import-graph=
+
+# Output a graph (.gv or any supported image format) of all (i.e. internal and
+# external) dependencies to the given file (report RP0402 must not be
+# disabled).
+import-graph=
+
+# Output a graph (.gv or any supported image format) of internal dependencies
+# to the given file (report RP0402 must not be disabled).
+int-import-graph=
+
+# Force import order to recognize a module as part of the standard
+# compatibility libraries.
+known-standard-library=
+
+# Force import order to recognize a module as part of a third party library.
+known-third-party=enchant
+
+# Couples of modules and preferred modules, separated by a comma.
+preferred-modules=
+
+
+[LOGGING]
+
+# The type of string formatting that logging methods do. `old` means using %
+# formatting, `new` is for `{}` formatting.
+logging-format-style=old
+
+# Logging modules to check that the string format arguments are in logging
+# function parameter format.
+logging-modules=logging
+
+
+[MESSAGES CONTROL]
+
+# Only show warnings with the listed confidence levels. Leave empty to show
+# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE,
+# UNDEFINED.
+confidence=HIGH,
+ CONTROL_FLOW,
+ INFERENCE,
+ INFERENCE_FAILURE,
+ UNDEFINED
+
+# Disable the message, report, category or checker with the given id(s). You
+# can either give multiple identifiers separated by comma (,) or put this
+# option multiple times (only on the command line, not in the configuration
+# file where it should appear only once). You can also use "--disable=all" to
+# disable everything first and then re-enable specific checks. For example, if
+# you want to run only the similarities checker, you can use "--disable=all
+# --enable=similarities". If you want to run only the classes checker, but have
+# no Warning level messages displayed, use "--disable=all --enable=classes
+# --disable=W".
+disable=raw-checker-failed,
+ bad-inline-option,
+ locally-disabled,
+ file-ignored,
+ suppressed-message,
+ useless-suppression,
+ deprecated-pragma,
+ use-symbolic-message-instead,
+ use-implicit-booleaness-not-comparison-to-string,
+ use-implicit-booleaness-not-comparison-to-zero
+
+# Enable the message, report, category or checker with the given id(s). You can
+# either give multiple identifier separated by comma (,) or put this option
+# multiple time (only on the command line, not in the configuration file where
+# it should appear only once). See also the "--disable" option for examples.
+enable=
+
+
+[METHOD_ARGS]
+
+# List of qualified names (i.e., library.method) which require a timeout
+# parameter e.g. 'requests.api.get,requests.api.post'
+timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request
+
+
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,
+ XXX,
+ TODO
+
+# Regular expression of note tags to take in consideration.
+notes-rgx=
+
+
+[REFACTORING]
+
+# Maximum number of nested blocks for function / method body
+max-nested-blocks=5
+
+# Complete name of functions that never returns. When checking for
+# inconsistent-return-statements if a never returning function is called then
+# it will be considered as an explicit return statement and no message will be
+# printed.
+never-returning-functions=sys.exit,argparse.parse_error
+
+
+[REPORTS]
+
+# Python expression which should return a score less than or equal to 10. You
+# have access to the variables 'fatal', 'error', 'warning', 'refactor',
+# 'convention', and 'info' which contain the number of messages in each
+# category, as well as 'statement' which is the total number of statements
+# analyzed. This score is used by the global evaluation report (RP0004).
+evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10))
+
+# Template used to display messages. This is a python new-style format string
+# used to format the message information. See doc for all details.
+msg-template=
+
+# Set the output format. Available formats are: text, parseable, colorized,
+# json2 (improved json format), json (old json format) and msvs (visual
+# studio). You can also give a reporter class, e.g.
+# mypackage.mymodule.MyReporterClass.
+#output-format=
+
+# Tells whether to display a full report or only the messages.
+reports=no
+
+# Activate the evaluation score.
+score=yes
+
+
+[SIMILARITIES]
+
+# Comments are removed from the similarity computation
+ignore-comments=yes
+
+# Docstrings are removed from the similarity computation
+ignore-docstrings=yes
+
+# Imports are removed from the similarity computation
+ignore-imports=yes
+
+# Signatures are removed from the similarity computation
+ignore-signatures=yes
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+
+[SPELLING]
+
+# Limits count of emitted suggestions for spelling mistakes.
+max-spelling-suggestions=4
+
+# Spelling dictionary name. No available dictionaries : You need to install
+# both the python package and the system dependency for enchant to work.
+spelling-dict=
+
+# List of comma separated words that should be considered directives if they
+# appear at the beginning of a comment and should not be checked.
+spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:
+
+# List of comma separated words that should not be checked.
+spelling-ignore-words=
+
+# A path to a file that contains the private dictionary; one word per line.
+spelling-private-dict-file=
+
+# Tells whether to store unknown words to the private dictionary (see the
+# --spelling-private-dict-file option) instead of raising a message.
+spelling-store-unknown-words=no
+
+
+[STRING]
+
+# This flag controls whether inconsistent-quotes generates a warning when the
+# character used as a quote delimiter is used inconsistently within a module.
+check-quote-consistency=no
+
+# This flag controls whether the implicit-str-concat should generate a warning
+# on implicit string concatenation in sequences defined over several lines.
+check-str-concat-over-line-jumps=no
+
+
+[TYPECHECK]
+
+# List of decorators that produce context managers, such as
+# contextlib.contextmanager. Add to this list to register other decorators that
+# produce valid context managers.
+contextmanager-decorators=contextlib.contextmanager
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E1101 when accessed. Python regular
+# expressions are accepted.
+generated-members=
+
+# Tells whether to warn about missing members when the owner of the attribute
+# is inferred to be None.
+ignore-none=yes
+
+# This flag controls whether pylint should warn about no-member and similar
+# checks whenever an opaque object is returned when inferring. The inference
+# can return multiple potential results while evaluating a Python object, but
+# some branches might not be evaluated, which results in partial inference. In
+# that case, it might be useful to still emit no-member and other checks for
+# the rest of the inferred objects.
+ignore-on-opaque-inference=yes
+
+# List of symbolic message names to ignore for Mixin members.
+ignored-checks-for-mixins=no-member,
+ not-async-context-manager,
+ not-context-manager,
+ attribute-defined-outside-init
+
+# List of class names for which member attributes should not be checked (useful
+# for classes with dynamically set attributes). This supports the use of
+# qualified names.
+ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace
+
+# Show a hint with possible names when a member name was not found. The aspect
+# of finding the hint is based on edit distance.
+missing-member-hint=yes
+
+# The minimum edit distance a name should have in order to be considered a
+# similar match for a missing member name.
+missing-member-hint-distance=1
+
+# The total number of similar names that should be taken in consideration when
+# showing a hint for a missing member.
+missing-member-max-choices=1
+
+# Regex pattern to define which classes are considered mixins.
+mixin-class-rgx=.*[Mm]ixin
+
+# List of decorators that change the signature of a decorated function.
+signature-mutators=
+
+
+[VARIABLES]
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid defining new builtins when possible.
+additional-builtins=
+
+# Tells whether unused global variables should be treated as a violation.
+allow-global-unused-variables=yes
+
+# List of names allowed to shadow builtins
+allowed-redefined-builtins=
+
+# List of strings which can identify a callback function by name. A callback
+# name must start or end with one of those strings.
+callbacks=cb_,
+ _cb
+
+# A regular expression matching the name of dummy variables (i.e. expected to
+# not be used).
+dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
+
+# Argument names that match this expression will be ignored.
+ignored-argument-names=_.*|^ignored_|^unused_
+
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
+
+# List of qualified module names which can have objects that can redefine
+# builtins.
+redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
diff --git a/.travis.yml b/.travis.yml
index 74d51e7da..71328f1d0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,10 @@
language: python
python:
- - "3.7"
+ - "3.11"
+ - "3.10"
+ - "3.9"
+ - "3.8"
+ - "3.7"
install:
- pip install .
- pip install -r requirements.txt
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c9af5982f..382f54649 100755
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,10 +1,10 @@
-# Contributing to MyDollarBot
+# Contributing to DollarSplitBot
-Follow the set of guidelines below to contribute to MyDollarBot!
+Follow the set of guidelines below to contribute to DollarSplitBot!
## Code of Conduct
-This project and everyone participating in it is governed by the [Code of Conduct](https://github.com/usmanwardag/dollar_bot/blob/main/CODE_OF_CONDUCT.md). By participating, you are expected to upload this code. Please report unacceptable behavior to sbose2@ncsu.edu.
+This project and everyone participating in it is governed by the [Code of Conduct](https://github.com/shonilbhide/dollar_bot/blob/main/CODE_OF_CONDUCT.md). By participating, you are expected to upload this code. Please report unacceptable behavior to csc510group32@gmail.com.
Prerequistes required before starting this project:
@@ -41,7 +41,7 @@ A cursory search is necessary to check if the reported bug is already mentioned
## To Submit A Good Bug Report
-[GitHub issues](https://github.com/usmanwardag/dollar_bot/issues) You can track the bugs from this. For the repository that has bug, create an issue and fill out [the template](https://github.com/atom/.github/blob/master/.github/ISSUE_TEMPLATE/bug_report.md) to give details of the bug.
+[GitHub issues](https://github.com/shonilbhide/dollar_bot/issues) You can track the bugs from this. For the repository that has bug, create an issue and fill out [the template](https://github.com/atom/.github/blob/master/.github/ISSUE_TEMPLATE/bug_report.md) to give details of the bug.
* To identify the problem, give the issue a clear and informative term.
@@ -70,7 +70,7 @@ Check out this [debugging guide](https://flight-manual.atom.io/hacking-atom/sect
## To Submit A Good Enhancement Suggestion
-[GitHub issues](https://github.com/usmanwardag/dollar_bot/issues) You can track the bugs from this. For the repository that has bug, create an issue and fill out [the template](https://github.com/atom/.github/blob/master/.github/ISSUE_TEMPLATE/bug_report.md) to give details of the bug.
+[GitHub issues](https://github.com/shonilbhide/dollar_bot/issues) You can track the bugs from this. For the repository that has bug, create an issue and fill out [the template](https://github.com/atom/.github/blob/master/.github/ISSUE_TEMPLATE/bug_report.md) to give details of the bug.
* To identify the problem, give the issue a clear and informative term.
* Describe in as much detail as possible to duplicate the problem. Explain the problem and explain about the exact command sused in the terminal which caus ethe bug to occur.
diff --git a/CSC510 Group 32 - DollarSplitBot.csv b/CSC510 Group 32 - DollarSplitBot.csv
new file mode 100644
index 000000000..4c1f6ac29
--- /dev/null
+++ b/CSC510 Group 32 - DollarSplitBot.csv
@@ -0,0 +1,102 @@
+https://github.com/shonilbhide/dollar_bot/tree/dev,,
+Item,Assessment,Evidence
+Video,3,VIDEO
+"Workload is spread over the whole team (one team member is often Xtimes more productive than the others...but nevertheless, here is a track record that everyone is contributing a lot)",3,https://github.com/shonilbhide/dollar_bot/pulse
+Number of commits,3,https://github.com/shonilbhide/dollar_bot/graphs/commit-activity
+Number of commits: by different people,3,Multiple commits from 4 contributors
+Issues reports: there are many,3,https://github.com/shonilbhide/dollar_bot/issues
+Issues are being closed,3,https://github.com/users/shonilbhide/projects/2
+DOI badge: exists,3,https://zenodo.org/records/10015948
+"Docs: doco generated, format not ugly",3,Generated using Pycco
+Docs: what: point descriptions of each class/function (in isolation),3,https://github.com/shonilbhide/dollar_bot/blob/dev/docs/Update_Version.pdf
+"Docs: how: for common use cases X,Y,Z mini-tutorials showing worked examples on how to do X,Y,Z",3,VIDEO
+"Docs: why: docs tell a story, motivate the whole thing, deliver a punchline that makes you want to rush out and use the thing",3,https://github.com/shonilbhide/dollar_bot/blob/Issue_84_Documentation/README.md#Why-should-you-use-DollarSplitBot?
+"Docs: short video, animated, hosted on your repo. That convinces people why they want to work on your code.",3,VIDEO
+Use of version control tools,3,Git is used for the project through out
+Use of style checkers,3,https://github.com/shonilbhide/dollar_bot/blob/dev/.pylintrc
+Use of code formatters.,3,https://github.com/shonilbhide/dollar_bot/blob/dev/.travis.yml
+Use of syntax checkers.,3,https://github.com/shonilbhide/dollar_bot/blob/dev/.pylintrc
+Use of code coverage,3,dollar_bot/codecov.yml at main · usmanwardag/dollar_bot (github.com)
+Other automated analysis tools,3,https://github.com/shonilbhide/dollar_bot/tree/dev#automated-analysis-tools
+Test cases exist,3,https://github.com/shonilbhide/dollar_bot/tree/dev/test
+Test cases are routinely executed,1,
+The files CONTRIBUTING.md lists coding standards and lots of tips on how to extend the system without screwing things up,3,https://github.com/shonilbhide/dollar_bot/blob/main/CONTRIBUTING.md
+Issues are discussed before they are closed,2,Comments by other contributors present for a few issues
+Chat channel: exists,1,https://github.com/discussions
+Test cases: a large proportion of the issues related to handling failing cases,2,https://github.com/shonilbhide/dollar_bot/tree/main/test
+Evidence that the whole team is using the same tools: everyone can get to all tools and files,3,https://github.com/shonilbhide/dollar_bot
+"Evidence that the whole team is using the same tools (e.g. config files in the repo, updated by lots of different people)",3,"one language and many contributors, with same requirements file.Hence, can assume so"
+"Evidence that the whole team is using the same tools (e.g. tutor can ask anyone to share screen, they demonstrate the system running on their computer)",3,Multiple branches can be created and contibutor can pull from any branch to modify
+Evidence that the members of the team are working across multiple places in the code base,3,https://github.com/users/shonilbhide/projects/2
+Short release cycles,3,https://github.com/shonilbhide/dollar_bot/pulse
+,,
+"Does your website and documentation provide a clear, high-level overview of your software?",Y,https://github.com/shonilbhide/dollar_bot/tree/main/docs
+Does your website and documentation clearly describe the type of user who should use your software?,Y,https://github.com/shonilbhide/dollar_bot/tree/main/docs
+Do you publish case studies to show how your software has been used by yourself and others?,Y,VIDEO
+Is the name of your project/software unique?,Y,https://github.com/shonilbhide/dollar_bot
+Is your project/software name free from trademark violations?,Y,
+Is your software available as a package that can be deployed without building it?,Y,
+Is your software available for free?,Y,
+"Is your source code publicly available to download, either as a downloadable bundle or via access to a source code repository?",Y,
+"Is your software hosted in an established, third-party repository likeGitHub (https://github.com)",Y,GitHub
+Is your documentation clearly available on your website or within your software?,Y,https://github.com/shonilbhide/dollar_bot/tree/main/docs
+"Does your documentation include a ""quick start"" guide, that provides a short overview of how to use your software with some basic examples of use?",Y,https://github.com/usmanwardag/dollar_bot/blob/main/docs/Updated_version.pdf
+"If you provide more extensive documentation, does this provide clear, step-by-step instructions on how to deploy and use your software?",Y,https://github.com/shonilbhide/dollar_bot#installation
+"Do you provide a comprehensive guide to all your software’s commands, functions and options?",Y,https://github.com/usmanwardag/dollar_bot/blob/main/docs/Updated_version.pdf
+Do you provide troubleshooting information that describes the symptoms and step-by-step solutions for problems and error messages?,Y,https://github.com/usmanwardag/dollar_bot/blob/main/docs/Updated_version.pdf
+"If your software can be used as a library, package or service by other software, do you provide comprehensive API documentation?",NA,
+Do you store your documentation under revision control with your source code?,Y,
+"Do you publish your release history e.g. release data, version numbers, key features of each release etc. on your web site or in your documentation?",Y,https://github.com/shonilbhide/dollar_bot/releases/tag/v1.1.0
+Does your software describe how a user can get help with using your software?,Y,https://github.com/usmanwardag/dollar_bot/blob/main/docs/Updated_version.pdf
+"Does your website and documentation describe what support, if any, you provide to users and developers?",Y,
+Does your project have an e-mail address or forum that is solely for supporting users?,Y,https://github.com/shonilbhide/dollar_bot/tree/dev#contact-us
+Are e-mails to your support e-mail address received by more than one person?,Y,
+Does your project have a ticketing system to manage bug reports and feature requests?,Y,https://github.com/users/shonilbhide/projects/2
+"Is your project's ticketing system publicly visible to your users, so they can view bug reports and feature requests?",Y,https://github.com/users/shonilbhide/projects/2
+Is your software’s architecture and design modular?,Y,
+Does your software use an accepted coding standard or convention?,Y,
+"Does your software allow data to be imported and exported using open data formats? e.g. GIF, SVG, HTML, XML, tar, zip, CSV, JSON, NetCDF, or domain specific ones",Y,
+"Does your software allow communications using open communications protocols? e.g. HTTP, FTP, XMPP, SOAP over HTTP, or domain-specific ones",Y,
+"Is your software cross-platform compatible? e.g. does it run under two or more of Windows, Unix/Linux and Mac OS X, or can be used from within two or more of Internet Explorer, Chrome, Firefox and Safari?",Y,
+Does your software adhere to appropriate accessibility conventions or standards?,Y,
+Does your documentation adhere to appropriate accessibility conventions or standards?,Y,
+Is your source code stored in a repository under revision control?,Y,
+Is each source code release a snapshot of the repository?,Y,
+Are releases tagged in the repository?,Y,
+"Is there a branch of the repository that is always stable? (i.e. tests always pass, code always builds successfully)",Y,dev
+Do you back-up your repository?,Y,dev
+Do you provide publicly-available instructions for building your software from the source code?,Y,
+"Can you build, or package, your software using an automated tool?",Y,
+Do you provide publicly-available instructions for deploying your software?,Y,
+Does your documentation list all third-party dependencies?,Y,
+Does your documentation list the version number for all third-party dependencies?,Y,
+"Does your software list the web address, and licences for all third-party dependencies and say whether the dependencies are mandatory or optional?",Y,
+Can you download dependencies using a dependency management tool or package manager?,N,
+Do you have tests that can be run after your software has been built or deployed to show whether the build or deployment has been successful?,N,
+Do you have an automated test suite for your software?,Y,
+Do you have a framework to periodically (e.g. nightly) run your tests on the latest version of the source code?,Y,
+"Do you use continuous integration, automatically running tests whenever changes are made to your source code?",Y,
+Are your test results publicly visible,Y,
+Are all manually-run tests documented?,Y,
+"Does your project have resources (e.g. blog, Twitter, RSS feed, Facebook page, wiki, mailing list) that are regularly updated with information about your software?",N,
+Does your website state how many projects and users are associated with your project?,Y,https://github.com/shonilbhide/dollar_bot/tree/dev#number-of-projects-and-users-associated-with-the-project
+Do you provide success stories on your website?,N,
+Do you list your important partners and collaborators on your website?,Y,
+Do you list your project's publications on your website or link to a resource where these are available?,Y,
+Do you list third-party publications that refer to your software on your website or link to a resource where these are available?,Y,
+Can users subscribe to notifications to changes to your source code repository?,N,
+"If your software is developed as an open source project (and, not just a project developing open source software), do you have a governance model?",Y,
+"Do you accept contributions (e.g. bug fixes, enhancements, documentation updates, tutorials) from people who are not part of your project?",Y,
+Do you have a contributions policy?,Y,
+Is your contributions' policy publicly available?,Y,
+Do contributors keep the copyright/IP of their contributions?,Y,
+Does your website and documentation clearly state the copyright owners of your software and documentation?,Y,
+Does each of your source code files include a copyright statement?,Y,
+Does your website and documentation clearly state the licence of your software?,Y,MIT Licence
+Is your software released under an open source licence?,Y,
+Is your software released under an OSI-approved open-source licence?,Y,MIT Licence
+Does each of your source code files include a licence header?,N,
+Do you have a recommended citation for your software?,Y,
+"Does your website or documentation include a project roadmap (a list of project and development milestones for the next 3, 6 and 12 months)?",Y,
+"Does your website or documentation describe how your project is funded, and the period over which funding is guaranteed?",NA,
+"Do you make timely announcements of the deprecation of components, APIs, etc.?",Y,https://github.com/shonilbhide/dollar_bot/tree/dev#depriciated-libraries
\ No newline at end of file
diff --git a/README.md b/README.md
index 8e27f5f57..40fd8cbad 100644
--- a/README.md
+++ b/README.md
@@ -1,21 +1,29 @@
-# 💰 MyDollar Bot 💰
+# 💰 DollarSplitBot 💰
Table of Contents
- - Why should you use Dollar Bot?
+ - Why should you use DollarSplitBot?
- Check out the video!
- What is new in this version?
- Installation
+ - How To Run
+ - Configuring Email Credentials for SMTP: Sending Emails from Your Account
- Testing
- Code Coverage
+ - Use Cases
+ - Automated Analysis Tools
- License
- Code Documentation
+ - Version Specifications
- How to Contribute
+ - Depriciated Libraries
- Future RoadMap
- - Contributors
- - Acknowledgements
+ - Number of projects and Users associated with the project
+ - Contributors
+ - Acknowledgements
+ - Contact Us
@@ -26,40 +34,48 @@
-[](https://GitHub.com/usmanwardag/auto_anki)
+[](https://github.com/shonilbhide/dollar_bot)

[](https://desktop.telegram.org/)

-[](https://github.com/sak007/MyDollarBot-BOTGo/graphs/contributors)
-[](https://doi.org/10.5281/zenodo.5759217)
-[](https://app.travis-ci.com/usmanwardag/dollar_bot)
+[](https://github.com/shonilbhide/dollar_bot/graphs/contributors)
+[](https://zenodo.org/records/10015948)
+[](https://dl.circleci.com/status-badge/redirect/circleci/KRJsvuprWQqWTJMZXoedkH/S11gsHj3tEGpX8YLaeYmJ5/tree/main)
[](https://codecov.io/gh/usmanwardag/dollar_bot)
-[](https://github.com/sak007/MyDollarBot-BOTGo/issues?q=is%3Aopen+is%3Aissue)
-[](https://github.com/sak007/MyDollarBot-BOTGo/issues?q=is%3Aissue+is%3Aclosed)
-
+[](https://github.com/shonilbhide/dollar_bot/issues)
+[](https://github.com/shonilbhide/dollar_bot/issues?q=is%3Aissue+is%3Aclosed)
-## Why should you use MyDollar Bot?
+## Why should you use DollarSplitBot?
-Dollar Bot is an easy-to-use Telegram Bot that assists you in recording your daily expenses on a local system without any hassle.
-With simple commands, this bot allows you to:
-- Add/Record new spendings
-- Display your spendings through bar graph
-- Show the sum of your expenditure for the current day/month
-- Display your spending history
-- Clear/Erase all your records
-- Edit/Change any spending details if you wish to
+"Discover a whole new level of financial clarity and fairness – where you'll never have to wonder 'Who owes me, and who do I owe?' again. Say goodbye to financial puzzles, and embrace our extended expense management system to reclaim your peace of mind!"
+
+Introducing DollarSplitBot, your trusty companion on Telegram, here to turn the mundane task of tracking your daily expenses into a breeze. This ingenious bot simplifies the process of keeping tabs on your spending, even when you're offline. But that's not all; it's also your go-to solution for managing group expenses and ensuring everyone's financial equilibrium.
+
+With just a few swift commands, DollarSplitBot empowers you to:
+
+1. **Welcome New Faces:** Add your friends to share expenses with ease.
+2. **Log Your Transactions:** Document and store your expenditures effortlessly.
+3. **Equitable Divisions:** Showcase your spending history, unraveling who owes what to whom.
+4. **Money Matters:** Keep tabs on your daily and monthly expenditure totals.
+5. **Your Financial Story:** Access your spending history at any time.
+6. **A Clean Slate:** Erase all records when it's time to start anew.
+7. **Tailored Details:** Edit any spending particulars to your liking.
+8. **Paper Trail:** Generate sleek PDF expenditure reports for a comprehensive overview.
+9. **Friendly Nudges:** Send friendly reminders via email to ensure financial settlements.
+
+DollarSplitBot: Where simplicity meets financial harmony at your fingertips.
## Check out the video!
-[](https://youtu.be/aCjcT1CHAzU)
+To demonstrate our application's functionality and showcase its working examples, we have produced a YouTube video for the DollarSplitBot project. In this video, we showcase that the system operates as intended. You can view the video by clicking on the following link: [YouTube Link](https://www.youtube.com/watch?v=JT06PTMHz7Y)
## What is new in this version?
-Checkout the [this documentation](https://github.com/usmanwardag/dollar_bot/blob/main/docs/Updated_version.pdf)
+Checkout the [this documentation](docs/Update_Version.pdf)
## Installation
The below instructions can be followed in order to set-up this bot at your end in a span of few minutes! Let's get started:
@@ -105,6 +121,26 @@ A successful run will generate a message on your terminal that says "TeleBot: St
To run the script automatically at startup / reboot, simply add the `.run_forever.sh` script to your `.bashrc` file, which executes whenever you reboot your system.
+## Configuring Email Credentials for SMTP: Sending Emails from Your Account
+
+**SMTP (Simple Mail Transfer Protocol)** is a standard protocol for sending emails. It is widely used for sending email messages from one server to another. In the code, we are using SMTP to send emails via a Gmail account. Here's how the SMTP configuration and usage work:
+
+1. **SMTP Server**: The `smtp_server` variable is set to 'smtp.gmail.com,' which is the SMTP server for Gmail. This server is responsible for sending your email messages.
+
+2. **SMTP Port**: The `smtp_port` variable is set to 587. This is the port for TLS (Transport Layer Security) encryption. Gmail uses this port for secure email communication.
+
+3. **SMTP Username**: The smtp_username variable should be set to your own Gmail email address from which you want to send the emails. Make sure to replace `your-email@gmail.com` with your actual Gmail email address in the code. This ensures that the emails will be sent from your specific Gmail account.
+
+4. **SMTP Password**: The `smtp_password` variable is set, you need to generate an "App Password". An App Password is a 16-character code that allows you to access your Gmail account without revealing your real password. To generate an App Password, follow these steps:
+
+ - Go to your Google Account settings (https://myaccount.google.com/).
+ - In the "Security" section, under "Signing in to Google", select "App Passwords".
+ - Select "Mail" and "Other (Custom name)" from the dropdown menus.
+ - Click "Generate".
+ - Google will provide you with a 16-character App Password. Use this as your `smtp_password` in your code.
+
+By customizing these settings, you can send emails from any email account using SMTP. Just ensure you are adhering to the security guidelines provided by your email provider.
+
## Testing
We use pytest to perform testing on all unit tests together. The command needs to be run from the home directory of the project. The command is:
@@ -123,39 +159,84 @@ coverage run -m pytest test/
coverage report
```
+## Use Cases
+
+Common use cases for DollarSplitBot summarized in three points:
+
+1. **Personal Expense Tracking:**
+ - Easily log and monitor your individual daily and monthly expenses, including groceries, dining out, transportation, and entertainment.
+ - Access your spending history and totals at any time, providing valuable insights into your financial habits.
+
+2. **Group Expense Management:**
+ - Efficiently manage group expenses with friends or family members. Add participants to track shared costs and responsibilities.
+ - DollarSplitBot calculates equitable divisions, simplifying the process of determining who owes what to whom in group expenses.
+
+3. **Expense Reporting and Communication:**
+ - Generate detailed PDF expenditure reports for a comprehensive overview of your financial activity.
+ - Utilize the bot's email reminders to facilitate financial settlements and maintain harmony in shared expenses, ensuring everyone is accountable.
+
+Certainly, you can watch this video [YouTube Link](https://youtu.be/JT06PTMHz7Y) for a step-by-step guide on how to use DollarSplitBot.
+
+## Automated Analysis Tools
+
+This project uses various automated analysis tools like
+- pylint and flask8 for code formating
+- pytest for tesing
+- coverage.py for code coverage
+- Travis CI for automated testing
+
## License
-This project is licensed under the terms of the MIT license. Please check [License](https://github.com/usmanwardag/dollar_bot/blob/main/LICENSE) for more details.
+This project is licensed under the terms of the MIT license. Please check [License](https://github.com/shonilbhide/dollar_bot/blob/main/LICENSE) for more details.
## Code Documentation
-Checkout the [docs](https://github.com/sak007/MyDollarBot-BOTGo/tree/main/docs)
+Checkout the [docs](https://github.com/shonilbhide/dollar_bot/tree/main/docs)
+
+## Version Specifications
+The current release of the project has the following versions:
+- python- Python 3.9.1
+- pip- pip 23.2.1
+
## How to Contribute
-We would be happy to receive contributions! If you'd like to, please go through our [CONTRIBUTING.md](https://github.com/usmanwardag/dollar_bot/blob/main/CONTRIBUTING.md)
+We would be happy to receive contributions! If you'd like to, please go through our [CONTRIBUTING.md](https://github.com/shonilbhide/dollar_bot/blob/main/CONTRIBUTING.md)
-For any feedback, issues, or bug reports, please create an issue [here](https://github.com/usmanwardag/dollar_bot/issues/new).
+For any feedback, issues, or bug reports, please create an issue [here](https://github.com/shonilbhide/dollar_bot/issues/new).
+## Depriciated Libraries
+- "The parameter "none_stop" is deprecated. Use "non_stop" instead."
## Future RoadMap
- More content can be added for the way notifications can be displayed on the user front. This can be done to make the UI more interactive.
- Recurring expenses feature can be added for faster addition of expenses instead of following the whole process of everytime.
+- This application can be integrated with a group chat to track expenses of a group.
+- A better model can be implemented to forecast the budgets and expenses for future.
+- Make our bot support multiple languages, and not just english so that it might be helpful in the other regions of the world.
+- Integrate the bot with financial services, like bank APIs, for real-time expense tracking and account balance updates.
+- Implement a reminder system to notify users of recurring expenses, upcoming bills, or when they need to settle debts.
+
+
+## Number of projects and Users associated with the project
+Here is the list of projects and the users associated with the project:
+- Project 1[Project1] (https://github.com/shonilbhide/dollar_bot) and users: Shonil_Bhide, Rutuja_Rashinkar, Sakshi_Basapure Akshada_Malpure
+- Project 2 [Project2] (https://github.com/usmanwardag/dollar_bot) and users: Usman_Khan, Aakriti_Aakriti, Suneha_Bose, Muskan_Gupta, Kriti_Khullar
+- Project 3 [Project3] (https://github.com/sak007/MyDollarBot-BOTGo) and users: Athithya, Subramanian, Ashok, Zunaid, Rithik, Dev, Prakruthi, Radhika, Rohan, Sunidhi
+- Project 4 [Project4] (https://github.com/deekay2310/MyDollarBot) and users:Dev, Prakruthi, Radhika, Rohan, Sunidhi
+
## Contributors
-
-
## Acknowledgements
- We would like to express our gratitude 🙏🏻 and a big thank you 😇 to Prof. Dr. Timothy Menzie for giving us the opportunity to get into the shoes of software building and learning new skills and development process throught the project building.
@@ -163,6 +244,5 @@ For any feedback, issues, or bug reports, please create an issue [here](https://
- Thank you to the previous team 😊 for a thorough ReadMe and deatiled documentation.[MyDollarBot](https://github.com/sak007/MyDollarBot-BOTGo)
- Thank you to the ⭐️[Telegram bot](https://github.com/python-telegram-bot/python-telegram-bot)
-
-
-
+## Contact Us
+In case of any queries, kindly contact us on: csc510group32@gmail.com
diff --git a/RUBRICS.md b/RUBRICS.md
new file mode 100644
index 000000000..f7d197a9f
--- /dev/null
+++ b/RUBRICS.md
@@ -0,0 +1,103 @@
+|https://github.com/shonilbhide/dollar_bot/tree/dev | | |
+|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|------------------------------------------------------------------------------------------------------------------|
+|Item |Assessment|Evidence |
+|Video |3 |VIDEO |
+|Workload is spread over the whole team (one team member is often Xtimes more productive than the others...but nevertheless, here is a track record that everyone is contributing a lot) |3 |https://github.com/shonilbhide/dollar_bot/pulse |
+|Number of commits |3 |https://github.com/shonilbhide/dollar_bot/graphs/commit-activity |
+|Number of commits: by different people |3 |Multiple commits from 4 contributors |
+|Issues reports: there are many |3 |https://github.com/shonilbhide/dollar_bot/issues |
+|Issues are being closed |3 |https://github.com/users/shonilbhide/projects/2 |
+|DOI badge: exists |3 |https://zenodo.org/records/10015948 |
+|Docs: doco generated, format not ugly |3 |Generated using Pycco |
+|Docs: what: point descriptions of each class/function (in isolation) |3 |https://github.com/shonilbhide/dollar_bot/blob/dev/docs/Update_Version.pdf |
+|Docs: how: for common use cases X,Y,Z mini-tutorials showing worked examples on how to do X,Y,Z |3 |VIDEO |
+|Docs: why: docs tell a story, motivate the whole thing, deliver a punchline that makes you want to rush out and use the thing |3 |https://github.com/shonilbhide/dollar_bot/blob/Issue_84_Documentation/README.md#Why-should-you-use-DollarSplitBot?|
+|Docs: short video, animated, hosted on your repo. That convinces people why they want to work on your code. |3 |VIDEO |
+|Use of version control tools |3 |Git is used for the project through out |
+|Use of style checkers |3 |https://github.com/shonilbhide/dollar_bot/blob/dev/.pylintrc |
+|Use of code formatters. |3 |https://github.com/shonilbhide/dollar_bot/blob/dev/.travis.yml |
+|Use of syntax checkers. |3 |https://github.com/shonilbhide/dollar_bot/blob/dev/.pylintrc |
+|Use of code coverage |3 |dollar_bot/codecov.yml at main · usmanwardag/dollar_bot (github.com) |
+|Other automated analysis tools |3 |https://github.com/shonilbhide/dollar_bot/tree/dev#automated-analysis-tools |
+|Test cases exist |3 |https://github.com/shonilbhide/dollar_bot/tree/dev/test |
+|Test cases are routinely executed |1 | |
+|The files CONTRIBUTING.md lists coding standards and lots of tips on how to extend the system without screwing things up |3 |https://github.com/shonilbhide/dollar_bot/blob/main/CONTRIBUTING.md |
+|Issues are discussed before they are closed |2 |Comments by other contributors present for a few issues |
+|Chat channel: exists |1 |https://github.com/discussions |
+|Test cases: a large proportion of the issues related to handling failing cases |2 |https://github.com/shonilbhide/dollar_bot/tree/main/test |
+|Evidence that the whole team is using the same tools: everyone can get to all tools and files |3 |https://github.com/shonilbhide/dollar_bot |
+|Evidence that the whole team is using the same tools (e.g. config files in the repo, updated by lots of different people) |3 |one language and many contributors, with same requirements file.Hence, can assume so |
+|Evidence that the whole team is using the same tools (e.g. tutor can ask anyone to share screen, they demonstrate the system running on their computer) |3 |Multiple branches can be created and contibutor can pull from any branch to modify |
+|Evidence that the members of the team are working across multiple places in the code base |3 |https://github.com/users/shonilbhide/projects/2 |
+|Short release cycles |3 |https://github.com/shonilbhide/dollar_bot/pulse |
+| | | |
+|Does your website and documentation provide a clear, high-level overview of your software? |Y |https://github.com/shonilbhide/dollar_bot/tree/main/docs |
+|Does your website and documentation clearly describe the type of user who should use your software? |Y |https://github.com/shonilbhide/dollar_bot/tree/main/docs |
+|Do you publish case studies to show how your software has been used by yourself and others? |Y |VIDEO |
+|Is the name of your project/software unique? |Y |https://github.com/shonilbhide/dollar_bot |
+|Is your project/software name free from trademark violations? |Y | |
+|Is your software available as a package that can be deployed without building it? |Y | |
+|Is your software available for free? |Y | |
+|Is your source code publicly available to download, either as a downloadable bundle or via access to a source code repository? |Y | |
+|Is your software hosted in an established, third-party repository likeGitHub (https://github.com) |Y |GitHub |
+|Is your documentation clearly available on your website or within your software? |Y |https://github.com/shonilbhide/dollar_bot/tree/main/docs |
+|Does your documentation include a "quick start" guide, that provides a short overview of how to use your software with some basic examples of use? |Y |https://github.com/usmanwardag/dollar_bot/blob/main/docs/Updated_version.pdf |
+|If you provide more extensive documentation, does this provide clear, step-by-step instructions on how to deploy and use your software? |Y |https://github.com/shonilbhide/dollar_bot#installation |
+|Do you provide a comprehensive guide to all your software’s commands, functions and options? |Y |https://github.com/usmanwardag/dollar_bot/blob/main/docs/Updated_version.pdf |
+|Do you provide troubleshooting information that describes the symptoms and step-by-step solutions for problems and error messages? |Y |https://github.com/usmanwardag/dollar_bot/blob/main/docs/Updated_version.pdf |
+|If your software can be used as a library, package or service by other software, do you provide comprehensive API documentation? |NA | |
+|Do you store your documentation under revision control with your source code? |Y | |
+|Do you publish your release history e.g. release data, version numbers, key features of each release etc. on your web site or in your documentation? |Y |https://github.com/shonilbhide/dollar_bot/releases/tag/v1.1.0 |
+|Does your software describe how a user can get help with using your software? |Y |https://github.com/usmanwardag/dollar_bot/blob/main/docs/Updated_version.pdf |
+|Does your website and documentation describe what support, if any, you provide to users and developers? |Y | |
+|Does your project have an e-mail address or forum that is solely for supporting users? |Y |https://github.com/shonilbhide/dollar_bot/tree/dev#contact-us |
+|Are e-mails to your support e-mail address received by more than one person? |Y | |
+|Does your project have a ticketing system to manage bug reports and feature requests? |Y |https://github.com/users/shonilbhide/projects/2 |
+|Is your project's ticketing system publicly visible to your users, so they can view bug reports and feature requests? |Y |https://github.com/users/shonilbhide/projects/2 |
+|Is your software’s architecture and design modular? |Y | |
+|Does your software use an accepted coding standard or convention? |Y | |
+|Does your software allow data to be imported and exported using open data formats? e.g. GIF, SVG, HTML, XML, tar, zip, CSV, JSON, NetCDF, or domain specific ones |Y | |
+|Does your software allow communications using open communications protocols? e.g. HTTP, FTP, XMPP, SOAP over HTTP, or domain-specific ones |Y | |
+|Is your software cross-platform compatible? e.g. does it run under two or more of Windows, Unix/Linux and Mac OS X, or can be used from within two or more of Internet Explorer, Chrome, Firefox and Safari?|Y | |
+|Does your software adhere to appropriate accessibility conventions or standards? |Y | |
+|Does your documentation adhere to appropriate accessibility conventions or standards? |Y | |
+|Is your source code stored in a repository under revision control? |Y | |
+|Is each source code release a snapshot of the repository? |Y | |
+|Are releases tagged in the repository? |Y | |
+|Is there a branch of the repository that is always stable? (i.e. tests always pass, code always builds successfully) |Y |dev |
+|Do you back-up your repository? |Y |dev |
+|Do you provide publicly-available instructions for building your software from the source code? |Y | |
+|Can you build, or package, your software using an automated tool? |Y | |
+|Do you provide publicly-available instructions for deploying your software? |Y | |
+|Does your documentation list all third-party dependencies? |Y | |
+|Does your documentation list the version number for all third-party dependencies? |Y | |
+|Does your software list the web address, and licences for all third-party dependencies and say whether the dependencies are mandatory or optional? |Y | |
+|Can you download dependencies using a dependency management tool or package manager? |N | |
+|Do you have tests that can be run after your software has been built or deployed to show whether the build or deployment has been successful? |N | |
+|Do you have an automated test suite for your software? |Y | |
+|Do you have a framework to periodically (e.g. nightly) run your tests on the latest version of the source code? |Y | |
+|Do you use continuous integration, automatically running tests whenever changes are made to your source code? |Y | |
+|Are your test results publicly visible |Y | |
+|Are all manually-run tests documented? |Y | |
+|Does your project have resources (e.g. blog, Twitter, RSS feed, Facebook page, wiki, mailing list) that are regularly updated with information about your software? |N | |
+|Does your website state how many projects and users are associated with your project? |Y |https://github.com/shonilbhide/dollar_bot/tree/dev#number-of-projects-and-users-associated-with-the-project |
+|Do you provide success stories on your website? |N | |
+|Do you list your important partners and collaborators on your website? |Y | |
+|Do you list your project's publications on your website or link to a resource where these are available? |Y | |
+|Do you list third-party publications that refer to your software on your website or link to a resource where these are available? |Y | |
+|Can users subscribe to notifications to changes to your source code repository? |N | |
+|If your software is developed as an open source project (and, not just a project developing open source software), do you have a governance model? |Y | |
+|Do you accept contributions (e.g. bug fixes, enhancements, documentation updates, tutorials) from people who are not part of your project? |Y | |
+|Do you have a contributions policy? |Y | |
+|Is your contributions' policy publicly available? |Y | |
+|Do contributors keep the copyright/IP of their contributions? |Y | |
+|Does your website and documentation clearly state the copyright owners of your software and documentation? |Y | |
+|Does each of your source code files include a copyright statement? |Y | |
+|Does your website and documentation clearly state the licence of your software? |Y |MIT Licence |
+|Is your software released under an open source licence? |Y | |
+|Is your software released under an OSI-approved open-source licence? |Y |MIT Licence |
+|Does each of your source code files include a licence header? |N | |
+|Do you have a recommended citation for your software? |Y | |
+|Does your website or documentation include a project roadmap (a list of project and development milestones for the next 3, 6 and 12 months)? |Y | |
+|Does your website or documentation describe how your project is funded, and the period over which funding is guaranteed? |NA | |
+|Do you make timely announcements of the deprecation of components, APIs, etc.? |Y |https://github.com/shonilbhide/dollar_bot/tree/dev#depriciated-libraries |
diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md
new file mode 100644
index 000000000..be7f6891f
--- /dev/null
+++ b/TROUBLESHOOTING.md
@@ -0,0 +1,23 @@
+# Troubleshooting Guide
+
+## How to Access Your Gmail Account with an 'App Password'
+
+If you're having trouble accessing your Gmail account, you can use Google's 'App Password' solution to resolve the issue. Follow these steps to generate and use an app password:
+
+1. **Enable Two-Step Verification:**
+ - Go to your Google Account settings by visiting [Google Account](https://myaccount.google.com/).
+ - In the "Security" section, locate and select "Two-step verification."
+ - Follow the on-screen instructions to set up two-step verification for your account. This adds an extra layer of security.
+
+2. **Create an App Password:**
+ - After enabling two-step verification, navigate to your Google Account's security settings.
+ - In the "Signing in to Google" section, choose "App Passwords."
+ - Select "Mail" and "Other (Custom name)" from the respective dropdown menus.
+ - Click the "Generate" button.
+ - Google will provide you with a 16-character App Password. This password is a one-time use code that allows you to access your Gmail account without revealing your actual account password.
+
+3. **Use the App Password:**
+ - When configuring email settings or applications, use the same settings as you would for sending emails from your Gmail account.
+ - However, replace your regular password with the generated 16-character app password.
+ - This app password should be entered wherever you are prompted for your email password.
+
diff --git a/code/add.py b/code/add.py
index 906956ad1..d1ccef624 100644
--- a/code/add.py
+++ b/code/add.py
@@ -6,8 +6,6 @@
option = {}
-# === Documentation of add.py ===
-
def run(message, bot):
"""
@@ -17,48 +15,75 @@ def run(message, bot):
It takes 2 arguments for processing - message which is the message from the user,
and bot which is the telegram bot object from the main code.py function.
"""
- helper.read_json()
+ user_list=helper.read_json()
chat_id = message.chat.id
- option.pop(chat_id, None) # remove temp choice
+ owed_by =[]
+ # option.pop(chat_id, None) # remove temp choice
+
+ if str(chat_id) not in user_list:
+ user_list[str(chat_id)] = helper.createNewUserRecord(message)
markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
- markup.row_width = 2
- m = bot.send_message(chat_id, "Do you want to add a new category? Y/N")
- bot.register_next_step_handler(m, post_user_def_category, bot)
+ markup.row_width = len(user_list[str(chat_id)]["users"])
+ for c in user_list[str(chat_id)]["users"]:
+ markup.add(c)
+ m = bot.send_message(chat_id, "Select who paid for the Expense",reply_markup=markup)
+ bot.register_next_step_handler(m, select_user, bot,owed_by,user_list,None)
-def post_user_def_category(message, bot):
- markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
- markup.row_width = 2
+def select_user(message,bot,owed_by,user_list,paid_by):
chat_id = message.chat.id
- if str(message.text) == "Y" or str(message.text) == "y":
- message1 = bot.send_message(chat_id, "Please enter your category")
- bot.register_next_step_handler(message1, post_append_spend, bot)
+ text_m = message.text
+ remaining_users = [item for item in user_list[str(chat_id)]["users"] if item not in owed_by]
+ if len(remaining_users)==0:
+ post_append_spend(message,bot,owed_by,user_list,paid_by)
else:
- for c in helper.getSpendCategories():
+ if text_m in user_list[str(chat_id)]["users"]:
+ paid_by = text_m
+ markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
+ markup.row_width = 2
+
+ for c in remaining_users:
markup.add(c)
- msg = bot.reply_to(message, "Select Category", reply_markup=markup)
- bot.register_next_step_handler(msg, post_category_selection, bot)
+ m = bot.send_message(chat_id, "Select who shares the Expense",reply_markup=markup)
+ bot.register_next_step_handler(m, add_shared_user, bot,owed_by,user_list,paid_by)
+
+def add_shared_user(message,bot,owed_by,user_list,paid_by):
+ chat_id = message.chat.id
+ user = message.text
+ if user in user_list[str(chat_id)]["users"]:
+ owed_by.append(user)
+ else:
+ pass
+ choice = bot.reply_to(message, "Do you want to add more user to share the expense? Y/N")
+ bot.register_next_step_handler(choice, user_choice, bot, owed_by,user_list,paid_by)
+
+def user_choice(message, bot,owed_by, user_list,paid_by):
+ Choice = message.text
+ if Choice == "Y" or Choice == 'y':
+ select_user(message,bot,owed_by,user_list,paid_by)
+ elif Choice == "N" or Choice == 'n':
+ post_append_spend(message,bot,owed_by,user_list,paid_by)
-def post_append_spend(message, bot):
+def post_append_spend(message, bot,owed_by,user_list,paid_by):
+ chat_id = message.chat.id
markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
markup.row_width = 2
- selected_category = message.text
- helper.spend_categories.append(selected_category)
+ m = bot.send_message(chat_id, "Select a category")
for c in helper.getSpendCategories():
markup.add(c)
msg = bot.reply_to(message, "Select Category", reply_markup=markup)
- bot.register_next_step_handler(msg, post_category_selection, bot)
+ bot.register_next_step_handler(msg, post_category_selection, bot,owed_by,paid_by,user_list)
-def post_category_selection(message, bot):
+def post_category_selection(message, bot,owed_by,paid_by,user_list):
"""
post_category_selection(message, bot): It takes 2 arguments for processing -
message which is the message from the user, and bot which is the telegram bot object
- from the run(message, bot): function in the add.py file. It requests the user to enter the amount
- they have spent on the expense category chosen and then passes control to
- post_amount_input(message, bot): for further processing.
+ from the run(message, bot): function in the add.py file. It requests the user
+ to enter the amount they have spent on the expense category chosen and then passes
+ control to post_amount_input(message, bot): for further processing.
"""
try:
chat_id = message.chat.id
@@ -79,7 +104,7 @@ def post_category_selection(message, bot):
),
)
bot.register_next_step_handler(
- message, post_amount_input, bot, selected_category
+ message, post_amount_input, bot, selected_category,owed_by,paid_by,user_list
)
except Exception as e:
logging.exception(str(e))
@@ -97,7 +122,7 @@ def post_category_selection(message, bot):
bot.send_message(chat_id, display_text)
-def post_amount_input(message, bot, selected_category):
+def post_amount_input(message, bot, selected_category,owed_by,paid_by,user_list):
"""
post_amount_input(message, bot): It takes 2 arguments for processing -
message which is the message from the user, and bot which is the telegram bot
@@ -106,14 +131,8 @@ def post_amount_input(message, bot, selected_category):
calls add_user_record to store it.
"""
try:
- print("---------------------------------------------------")
-
chat_id = message.chat.id
- print(chat_id)
amount_entered = message.text
- print("0000000000000000000000000000000000000000000000000")
- print(amount_entered)
- print(selected_category)
amount_value = helper.validate_entered_amount(amount_entered) # validate
if amount_value == 0: # cannot be $0 spending
raise Exception("Spent amount has to be a non-zero number.")
@@ -130,7 +149,7 @@ def post_amount_input(message, bot, selected_category):
helper.write_json(
add_user_record(
- chat_id, "{},{},{}".format(date_str, category_str, amount_str)
+ user_list,message,chat_id, "{},{},{}".format(date_str, category_str, amount_str),amount_value,owed_by,paid_by
)
)
@@ -140,30 +159,37 @@ def post_amount_input(message, bot, selected_category):
amount_str, category_str, date_str
),
)
- helper.display_remaining_budget(message, bot, selected_category)
except Exception as e:
logging.exception(str(e))
bot.reply_to(message, "Oh no. " + str(e))
-def add_user_record(chat_id, record_to_be_added):
+def add_user_record(user_list,message,chat_id, record_to_be_added,amount_value,owed_by, paid_by):
"""
add_user_record(chat_id, record_to_be_added): Takes 2 arguments -
chat_id or the chat_id of the user's chat, and record_to_be_added which
is the expense record to be added to the store. It then stores this expense record in the store.
"""
- user_list = helper.read_json()
- print("!" * 5)
- print("before")
- print(user_list)
- print("!" * 5)
if str(chat_id) not in user_list:
- user_list[str(chat_id)] = helper.createNewUserRecord()
+ user_list[str(chat_id)] = helper.createNewUserRecord(message)
+ owed_amount = float(amount_value)/len(set(owed_by))
+ if "data" in user_list[str(chat_id)]:
+ user_list[str(chat_id)]["data"].append(record_to_be_added)
+ else:
+ user_list[str(chat_id)]["data"] = [record_to_be_added]
+ user_list[str(chat_id)]["owed"][paid_by] += float(amount_value)
+ for user in set(owed_by):
+ if user == paid_by:
+ user_list[str(chat_id)]["owed"][paid_by] -= owed_amount
+ elif paid_by in user_list[str(chat_id)]["owing"][user].keys():
+ user_list[str(chat_id)]["owing"][user][paid_by] += owed_amount
+ else:
+ user_list[str(chat_id)]["owing"][user][paid_by] = owed_amount
+ record_to_be_added+=",{},{}".format(paid_by,' & '.join(owed_by))
+ if "csv_data" in user_list[str(chat_id)]:
+ user_list[str(chat_id)]["csv_data"].append(record_to_be_added)
+ else:
+ user_list[str(chat_id)]["csv_data"] = [record_to_be_added]
+ return user_list
- user_list[str(chat_id)]["data"].append(record_to_be_added)
- print("!" * 5)
- print("after")
- print(user_list)
- print("!" * 5)
- return user_list
diff --git a/code/add_category.py b/code/add_category.py
new file mode 100644
index 000000000..777f0bb37
--- /dev/null
+++ b/code/add_category.py
@@ -0,0 +1,57 @@
+'''
+This is the main file used to implement the ADD CATEGORY feature.
+'''
+
+#import logging
+#from datetime import datetime
+import helper
+from telebot import types
+
+
+
+option = {}
+
+# === Documentation of add.py ===
+
+
+def run(message, bot):
+ """
+ run(message, bot): This is the main function used to implement the add feature.
+ It pop ups a menu on the bot asking the user to choose their expense category,
+ after which control is given to post_category_selection(message, bot) for further proccessing.
+ It takes 2 arguments for processing - message which is the message from the user,
+ and bot which is the telegram bot object from the main code.py function.
+ """
+ helper.read_json()
+ chat_id = message.chat.id
+ option.pop(chat_id, None) # remove temp choice
+ markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
+ markup.row_width = 2
+ message1 = bot.send_message(chat_id, "Please enter your category")
+ bot.register_next_step_handler(message1, post_append_spend, bot)
+
+def post_append_spend(message, bot):
+ chat_id = message.chat.id
+ markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
+ markup.row_width = 2
+ selected_category = message.text
+ if selected_category.lower() in [x.lower() for x in helper.spend_categories]:
+ bot.send_message(
+ chat_id, "Category already exists", reply_markup=types.ReplyKeyboardRemove()
+ )
+ message1 = bot.send_message(chat_id, "Please enter a new category")
+ bot.register_next_step_handler(message1, post_append_spend, bot)
+
+ else:
+ helper.spend_categories.append(selected_category)
+ user_list = helper.read_json()
+ user_list[str(chat_id)]["budget"]["category"][selected_category] = '0'
+ helper.write_json(user_list)
+ for c in helper.getSpendCategories():
+ markup.add(c)
+ bot.send_message(
+ chat_id,
+ "The following category has been added: {} ".format(
+ selected_category
+ ),
+ )
diff --git a/code/add_user.py b/code/add_user.py
new file mode 100644
index 000000000..a66effb48
--- /dev/null
+++ b/code/add_user.py
@@ -0,0 +1,78 @@
+'''
+This is the main file used to implement the REGISTER NEW USER feature.
+'''
+import logging
+import helper
+from telebot import types
+
+# Initialize a dictionary to store registered users
+registered_users = {}
+# user_list=helper.read_json()
+def register_people(message, bot,user_list):
+ chat_id = message.chat.id
+ if str(chat_id) not in user_list:
+ user_list[str(chat_id)] = helper.createNewUserRecord(message)
+ if "users" in user_list[str(chat_id)].keys():
+ registered_users={chat_id : user_list[str(chat_id)]["users"]}
+ else:
+ registered_users = {}
+ markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
+ markup.row_width = 2
+ msg = bot.send_message(chat_id, "Enter the name of the person you want to register:")
+ bot.register_next_step_handler(msg, add_person, bot,registered_users,user_list)
+
+def add_person(message, bot,registered_users,user_list):
+ chat_id = message.chat.id
+ name = message.text
+
+ # Check if the name is unique for this chat_id
+ if chat_id in registered_users and name in registered_users[chat_id]:
+ bot.send_message(chat_id, f"{name} is already registered.")
+ else:
+ if chat_id not in registered_users.keys():
+ registered_users[chat_id] = []
+
+ registered_users[chat_id].append(name)
+
+ bot.send_message(chat_id, f"{name} has been registered successfully!")
+
+ markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
+ markup.row_width = 2
+ markup.add("Register Another Person", "Finish Registration")
+ msg = bot.send_message(chat_id, "What would you like to do next?", reply_markup=markup)
+
+ bot.register_next_step_handler(msg, handle_registration_choice,
+ bot,registered_users,user_list)
+
+def handle_registration_choice(message, bot,registered_users,user_list):
+ chat_id = message.chat.id
+ choice = message.text
+
+ if choice == "Register Another Person":
+ msg = bot.send_message(chat_id, "Enter the name of the person you want to register:")
+ bot.register_next_step_handler(msg, add_person, bot,registered_users,user_list)
+ elif choice == "Finish Registration":
+ # Display the names of registered users
+ if chat_id in registered_users:
+ users = registered_users[chat_id]
+ user_list[str(chat_id)]["users"]=users
+ for user in users:
+ if str(chat_id) in user_list:
+ if "owed" in user_list[str(chat_id)]:
+ user_list[str(chat_id)]["owed"][user] = 0
+ else:
+ user_list[str(chat_id)]["owed"] = {user: 0}
+ if "owing" in user_list[str(chat_id)]:
+ user_list[str(chat_id)]["owing"][user] = {}
+ else:
+ user_list[str(chat_id)]["owing"] = {user: {}}
+ else:
+ user_list[str(chat_id)] = {"owed": {user: 0},"owing": {user: {}}}
+ helper.write_json(user_list)
+ if users:
+ bot.send_message(chat_id, "Registered Users:\n" +
+ '\n'.join(registered_users[chat_id]))
+ else:
+ bot.send_message(chat_id, "No users registered yet.")
+ else:
+ bot.send_message(chat_id, "Invalid choice. Please select a valid option.")
diff --git a/code/budget.py b/code/budget.py
index 0bd1359ae..06b73fd3b 100644
--- a/code/budget.py
+++ b/code/budget.py
@@ -1,21 +1,15 @@
+'''
+This is the main file used to implement the BUDGET feature.
+'''
+
import helper
+from telebot import types
+import logging
import budget_view
import budget_update
import budget_delete
-import logging
-from telebot import types
-
-# === Documentation of budget.py ===
-
def run(message, bot):
- """
- run(message, bot): This is the main function used to implement the budget feature.
- It pop ups a menu on the bot asking the user to choose to add, remove or display a budget,
- after which control is given to post_operation_selection(message, bot) for further proccessing.
- It takes 2 arguments for processing - message which is the message from the user, and bot which is the
- telegram bot object from the main code.py function.
- """
markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
options = helper.getBudgetOptions()
markup.row_width = 2
@@ -24,29 +18,29 @@ def run(message, bot):
msg = bot.reply_to(message, "Select Operation", reply_markup=markup)
bot.register_next_step_handler(msg, post_operation_selection, bot)
-
def post_operation_selection(message, bot):
- """
- post_operation_selection(message, bot): It takes 2 arguments for processing - message which
- is the message from the user, and bot which is the telegram bot object from the
- run(message, bot): function in the budget.py file. Depending on the action chosen by the user,
- it passes on control to the corresponding functions which are all located in different files.
- """
try:
chat_id = message.chat.id
+ user_list = helper.read_json()
op = message.text
options = helper.getBudgetOptions()
+
if op not in options.values():
- bot.send_message(
- chat_id, "Invalid", reply_markup=types.ReplyKeyboardRemove()
- )
+ bot.send_message(chat_id, "Invalid", reply_markup=types.ReplyKeyboardRemove())
raise Exception('Sorry I don\'t recognise this operation "{}"!'.format(op))
+ if str(chat_id) not in user_list:
+ # Initialize the user's data with an empty budget dictionary
+ user_list[str(chat_id)] = helper.createNewUserRecord(message)
+ if op == options["add"]:
+ budget_update.run(message, bot)
if op == options["update"]:
budget_update.run(message, bot)
elif op == options["view"]:
budget_view.run(message, bot)
elif op == options["delete"]:
budget_delete.run(message, bot)
+
+ helper.write_json(user_list)
+
except Exception as e:
- # print("hit exception")
- helper.throw_exception(e, message, bot, logging)
+ helper.throw_exception(e, message, bot, logging)
\ No newline at end of file
diff --git a/code/budget_delete.py b/code/budget_delete.py
index a4462563c..a42b24de2 100644
--- a/code/budget_delete.py
+++ b/code/budget_delete.py
@@ -1,22 +1,25 @@
-import helper
-
-# === Documentation of budget_delete.py ===
+'''
+This is the main file used to implement the DELETE BUDGET feature.
+'''
+import logging
+import helper
+from telebot import types
def run(message, bot):
- """
- run(message, bot): This is the main function used to implement the budget delete feature.
- It takes 2 arguments for processing - message which is the message from the user, and bot
- which is the telegram bot object from the main code.py function. It gets the user's chat ID
- from the message object, and reads all user data through the read_json method from the helper module.
- It then proceeds to empty the budget data for the particular user based on the user ID provided from the UI.
- It returns a simple message indicating that this operation has been done to the UI.
- """
chat_id = message.chat.id
user_list = helper.read_json()
- print(user_list)
- if str(chat_id) in user_list:
- user_list[str(chat_id)]["budget"]["overall"] = None
- user_list[str(chat_id)]["budget"]["category"] = None
+
+ if str(chat_id) not in user_list:
+ bot.send_message(chat_id, "You don't have budget data to delete.")
+ else:
+ if "budget" in user_list[str(chat_id)]:
+ # The 'budget' dictionary exists; you can proceed with deleting it
+ user_list[str(chat_id)]["budget"] = {"overall": None, "category": {}}
+ else:
+ bot.send_message(chat_id, "No budget data to delete.")
+
helper.write_json(user_list)
- bot.send_message(chat_id, "Budget deleted!")
+ bot.send_message(chat_id, "Budget data deleted successfully.")
+
+ helper.write_json(user_list)
diff --git a/code/budget_update.py b/code/budget_update.py
index df8041f4a..90fa097ab 100644
--- a/code/budget_update.py
+++ b/code/budget_update.py
@@ -1,217 +1,140 @@
-import helper
-import logging
-import budget_view
-from telebot import types
-
-# === Documentation of budget_update.py ===
+'''
+This is the main file used to implement the UPDATE BUDGET feature.
+'''
+from telebot import types
+import logging
+import helper
def run(message, bot):
- """
- run(message, bot): This is the main function used to implement the budget add/update features.
- It takes 2 arguments for processing - message which is the message from the user, and bot which
- is the telegram bot object from the main code.py function.
- """
chat_id = message.chat.id
- if helper.isOverallBudgetAvailable(chat_id):
- update_overall_budget(chat_id, bot)
- elif helper.isCategoryBudgetAvailable(chat_id):
- update_category_budget(message, bot)
- else:
- markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
- options = helper.getBudgetTypes()
- markup.row_width = 2
- for c in options.values():
- markup.add(c)
- msg = bot.reply_to(message, "Select Budget Type", reply_markup=markup)
- bot.register_next_step_handler(msg, post_type_selection, bot)
-
-
-def post_type_selection(message, bot):
- """
- post_type_selection(message, bot): It takes 2 arguments for processing - message
- which is the message from the user, and bot which is the telegram bot object.
- This function takes input from the user, making them choose which type of budget they
- would like to create - category-wise or overall, and then calls the corresponding functions for further processing.
- """
+ user_list = helper.read_json()
+
+ if str(chat_id) not in user_list:
+ # Initialize the user's data with an empty budget dictionary
+ user_list[str(chat_id)] = {"budget": {"overall": None, "category": {}}}
+
+ markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
+ budget_types = helper.getBudgetTypes()
+ markup.row_width = 2
+ for btype in budget_types.values():
+ markup.add(btype)
+ msg = bot.reply_to(message, "Select Budget Type", reply_markup=markup)
+ bot.register_next_step_handler(msg, set_budget_type, bot, user_list)
+
+def set_budget_type(message, bot, user_list):
try:
chat_id = message.chat.id
- op = message.text
+ budget_type = message.text
options = helper.getBudgetTypes()
- if op not in options.values():
- bot.send_message(
- chat_id, "Invalid", reply_markup=types.ReplyKeyboardRemove()
- )
- raise Exception('Sorry I don\'t recognise this operation "{}"!'.format(op))
- if op == options["overall"]:
- update_overall_budget(chat_id, bot)
- elif op == options["category"]:
- update_category_budget(message, bot)
+
+ if budget_type not in options.values():
+ bot.send_message(chat_id, "Invalid", reply_markup=types.ReplyKeyboardRemove())
+ raise Exception('Sorry I don\'t recognise this budget type "{}"!'.format(budget_type))
+
+ if budget_type == options["overall"]:
+ set_overall_budget(message, bot, user_list)
+ elif budget_type == options["category"]:
+ set_category_budget(message, bot, user_list)
+
except Exception as e:
helper.throw_exception(e, message, bot, logging)
+def set_overall_budget(message, bot, user_list):
+ chat_id = message.chat.id
-def update_overall_budget(chat_id, bot):
- """
- update_overall_budget(message, bot): It takes 2 arguments for processing - message which is the
- message from the user, and bot which is the telegram bot object. This function is called when the
- user wants to either create a new overall budget or update an existing one. It checks if there is an
- existing budget through the helper module's isOverallBudgetAvailable function and if so, displays this
- along with the prompt for the new (to be updated) budget, or just asks for the new budget. It passes control
- to the post_overall_amount_input function in the same file.
- """
- if helper.isOverallBudgetAvailable(chat_id):
- currentBudget = helper.getOverallBudget(chat_id)
- msg_string = "Current Budget is ${}\n\nHow much is your new monthly budget? \n(Enter numeric values only)"
- message = bot.send_message(chat_id, msg_string.format(currentBudget))
- else:
- message = bot.send_message(
- chat_id, "How much is your monthly budget? \n(Enter numeric values only)"
- )
- bot.register_next_step_handler(message, post_overall_amount_input, bot)
-
-
-def post_overall_amount_input(message, bot):
- """
- update_overall_budget(message, bot): It takes 2 arguments for processing -
- message which is the message from the user, and bot which is the telegram bot object.
- This function is called when the user wants to either create a new overall budget or
- update an existing one. It checks if there is an existing budget through the helper module's
- isOverallBudgetAvailable function and if so, displays this along with the prompt for the new
- (to be updated) budget, or just asks for the new budget. It passes control to the post_overall_amount_input
- function in the same file.
- """
+ if str(chat_id) not in user_list:
+ bot.send_message(chat_id, "You don't have budget data to set.")
+ return
+
try:
- chat_id = message.chat.id
- amount_value = helper.validate_entered_amount(message.text)
- if amount_value == 0:
- raise Exception("Invalid amount.")
- user_list = helper.read_json()
- if str(chat_id) not in user_list:
- user_list[str(chat_id)] = helper.createNewUserRecord()
- user_list[str(chat_id)]["budget"]["overall"] = amount_value
+ msg = bot.reply_to(message, "Enter the Overall Budget", reply_markup=types.ReplyKeyboardRemove())
+ bot.register_next_step_handler(msg, save_overall_budget, bot, user_list)
+ except Exception as e:
+ helper.throw_exception(e, message, bot, logging)
+
+def save_overall_budget(message, bot, user_list):
+ chat_id = message.chat.id
+ overall_budget = message.text
+
+ if not overall_budget:
+ bot.send_message(chat_id, "Budget not set. Please enter a valid budget.")
+ return
+
+ if str(chat_id) not in user_list:
+ bot.send_message(chat_id, "You don't have budget data to set.")
+ return
+
+ try:
+ # Ensure the 'budget' dictionary exists
+ if "budget" not in user_list[str(chat_id)]:
+ user_list[str(chat_id)]["budget"] = {"overall": None, "category": {}}
+
+ # Set the overall budget in the user's data
+ user_list[str(chat_id)]["budget"]["overall"] = float(overall_budget)
+
helper.write_json(user_list)
- bot.send_message(chat_id, "Budget Updated!")
- budget_view.display_overall_budget(message, bot)
- return user_list
+
+ bot.send_message(chat_id, f"Overall Budget set to ${str(overall_budget)}")
except Exception as e:
helper.throw_exception(e, message, bot, logging)
+def set_category_budget(message, bot, user_list):
+ chat_id = message.chat.id
-def update_category_budget(message, bot):
- """
- update_category_budget(message, bot): It takes 2 arguments for processing -
- message which is the message from the user, and bot which is the telegram bot object.
- This function is called in case the user decides to choose category-wise budgest in the run or
- post_type_selection stages. It gets the spend categories from the helper module's getSpendCategories
- and displays them to the user. It then passes control on to the post_category_selection function.
- """
- markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
- categories = helper.getSpendCategories()
- markup.row_width = 2
- for c in categories:
- markup.add(c)
- msg = bot.reply_to(message, "Select Category", reply_markup=markup)
- bot.register_next_step_handler(msg, post_category_selection, bot)
-
-
-def post_category_selection(message, bot):
- """
- post_category_selection(message, bot): It takes 2 arguments for processing -
- message which is the message from the user, and bot which is the telegram bot object.
- Based on the category chosen by the user, the bot checks if these are part of the pre-defined
- categories in helper.getSpendCategories(), else it throws an exception. If there is a budget
- already existing for the category, it identifies this case through helper.isCategoryBudgetByCategoryAvailable
- and shares this information with the user. If not, it simply proceeds. In either case, it then asks for the
- new/updated budget amount. It passes control onto post_category_amount_input.
- """
+ if str(chat_id) not in user_list:
+ bot.send_message(chat_id, "You don't have budget data to set.")
+ return
+
try:
- chat_id = message.chat.id
- selected_category = message.text
categories = helper.getSpendCategories()
- if selected_category not in categories:
- bot.send_message(
- chat_id, "Invalid", reply_markup=types.ReplyKeyboardRemove()
- )
- raise Exception(
- 'Sorry I don\'t recognise this category "{}"!'.format(selected_category)
- )
- if helper.isCategoryBudgetByCategoryAvailable(chat_id, selected_category):
- currentBudget = helper.getCategoryBudgetByCategory(
- chat_id, selected_category
- )
- msg_string = "Current monthly budget for {} is {}\n\nEnter monthly budget for {}\n(Enter numeric values only)"
- message = bot.send_message(
- chat_id,
- msg_string.format(selected_category, currentBudget, selected_category),
- )
- else:
- message = bot.send_message(
- chat_id,
- "Enter monthly budget for " + selected_category + "\n(Enter numeric values only)",
- )
- bot.register_next_step_handler(
- message, post_category_amount_input, bot, selected_category
- )
+ markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
+ markup.add(*categories)
+ msg = bot.reply_to(message, "Select a Category for Budget", reply_markup=markup)
+ bot.register_next_step_handler(msg, set_category_budget_amount, bot, user_list)
except Exception as e:
helper.throw_exception(e, message, bot, logging)
+def set_category_budget_amount(message, bot, user_list):
+ chat_id = message.chat.id
+ category = message.text
-def post_category_amount_input(message, bot, category):
- """
- post_category_amount_input(message, bot, category): It takes 2 arguments for
- processing - message which is the message from the user, and bot which is the telegram
- bot object, and the category chosen by the user.
- """
+ if category not in helper.getSpendCategories():
+ bot.send_message(chat_id, "Invalid category.", reply_markup=types.ReplyKeyboardRemove())
+ return
+
try:
- chat_id = message.chat.id
- amount_value = helper.validate_entered_amount(message.text)
- if amount_value == 0:
- raise Exception("Invalid amount.")
- user_list = helper.read_json()
- if str(chat_id) not in user_list:
- user_list[str(chat_id)] = helper.createNewUserRecord()
- if user_list[str(chat_id)]["budget"]["category"] is None:
+ msg = bot.reply_to(message, f"Enter Budget for {category}", reply_markup=types.ReplyKeyboardRemove())
+ bot.register_next_step_handler(msg, save_category_budget, bot, user_list, category)
+ except Exception as e:
+ helper.throw_exception(e, message, bot, logging)
+
+def save_category_budget(message, bot, user_list, category):
+ chat_id = message.chat.id
+ category_budget = message.text
+
+ if not category_budget:
+ bot.send_message(chat_id, "Budget not set. Please enter a valid budget.")
+ return
+
+ if str(chat_id) not in user_list:
+ bot.send_message(chat_id, "You don't have budget data to set.")
+ return
+
+ try:
+ # Ensure the 'budget' dictionary exists
+ if "budget" not in user_list[str(chat_id)]:
+ user_list[str(chat_id)]["budget"] = {"overall": None, "category": {}}
+
+ # Ensure the category budget dictionary exists
+ if "category" not in user_list[str(chat_id)]["budget"]:
user_list[str(chat_id)]["budget"]["category"] = {}
- user_list[str(chat_id)]["budget"]["category"][category] = amount_value
+
+ # Set the category budget in the user's data
+ user_list[str(chat_id)]["budget"]["category"][category] = float(category_budget)
+
helper.write_json(user_list)
- message = bot.send_message(
- chat_id, "Budget for " + category + " is now: $" + amount_value
- )
- post_category_add(message, bot)
+ bot.send_message(chat_id, f"Budget for {category} set to ${str(category_budget)}")
except Exception as e:
helper.throw_exception(e, message, bot, logging)
-
-
-def post_category_add(message, bot):
- """
- post_category_add(message, bot): It takes 2 arguments for processing -
- message which is the message from the user, and bot which is the telegram bot object.
- This exists in case the user wants to add a category-wise budget to another category after adding
- it for one category. It prompts the user to choose an option from helper.getUpdateOptions().values() and
- passes control to post_option_selection to either continue or exit the add/update feature.
- """
- markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
- options = helper.getUpdateOptions().values()
- markup.row_width = 2
- for c in options:
- markup.add(c)
- msg = bot.reply_to(message, "Select Option", reply_markup=markup)
- bot.register_next_step_handler(msg, post_option_selection, bot)
-
-
-def post_option_selection(message, bot):
- """
- post_option_selection(message, bot): It takes 2 arguments for processing -
- message which is the message from the user, and bot which is the telegram bot object.
- It takes the category chosen by the user from the message object. If the message is "continue",
- then it runs update_category_budget (above) allowing the user to get into the add/update process again.
- Otherwise, it exits the feature.
- """
- print("here")
- selected_option = message.text
- options = helper.getUpdateOptions()
- print("here")
- if selected_option == options["continue"]:
- update_category_budget(message, bot)
diff --git a/code/budget_view.py b/code/budget_view.py
index 077f8327b..91afac071 100644
--- a/code/budget_view.py
+++ b/code/budget_view.py
@@ -1,23 +1,12 @@
-import graphing
+'''
+This is the main file used to implement the VIEW BUDGET feature.
+'''
+
import helper
import logging
-import os
-
-# === Documentation of budget_view.py ===
-
def run(message, bot):
- """
- run(message, bot): This is the main function used to implement the budget feature.
- It takes 2 arguments for processing - message which is the message from the user, and bot which
- is the telegram bot object from the main code.py function. Depending on whether the user has configured
- an overall budget or a category-wise budget, this functions checks for either case using the helper
- module's isOverallBudgetAvailable and isCategoryBudgetAvailable functions and passes control on the
- respective functions(listed below). If there is no budget configured an exception is raised and the user
- is given a message indicating that there is no budget configured.
- """
try:
- print("here")
chat_id = message.chat.id
if helper.isOverallBudgetAvailable(chat_id):
display_overall_budget(message, bot)
@@ -30,29 +19,17 @@ def run(message, bot):
except Exception as e:
helper.throw_exception(e, message, bot, logging)
-
def display_overall_budget(message, bot):
- """
- display_overall_budget(message, bot): It takes 2 arguments for processing -
- message which is the message from the user, and bot which is the telegram bot
- object from the run(message, bot): in the same file. It gets the budget for the
- user based on their chat ID using the helper module and returns the same through the bot to the Telegram UI.
- """
chat_id = message.chat.id
data = helper.getOverallBudget(chat_id)
- bot.send_message(chat_id, "Overall Budget: $" + data)
+ if data is not None:
+ data = str(data) # Convert the float to a string
+ bot.send_message(chat_id, "Overall Budget: $" + str(data))
def display_category_budget(message, bot):
- """
- display_category_budget(message, bot): It takes 2 arguments for processing -
- message which is the message from the user, and bot which is the telegram bot object
- from the run(message, bot): in the same file. It gets the category-wise budget for the
- user based on their chat ID using the helper module.It then processes it into a string
- format suitable for display, and returns the same through the bot to the Telegram UI.
- """
chat_id = message.chat.id
data = helper.getCategoryBudget(chat_id)
- graphing.viewBudget(data)
- bot.send_photo(chat_id, photo=open("budget.png", "rb"))
- os.remove("budget.png")
+ if data is not None:
+ formatted_data = "\n".join([f"{category}: ${budget}" for category, budget in data.items()])
+ bot.send_message(chat_id, "Category-Wise Budgets:\n" + formatted_data)
diff --git a/code/code.py b/code/code.py
index 10bc43540..46176da36 100644
--- a/code/code.py
+++ b/code/code.py
@@ -1,8 +1,14 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
+'''
+Main program | to make the bot running
+'''
+
import logging
-import telebot
import time
+from datetime import datetime
+import telebot
+from jproperties import Properties
import helper
import edit
import history
@@ -11,15 +17,19 @@
import estimate
import delete
import add
+import add_category
+import delete_expense
+import send_mail
import budget
-from datetime import datetime
-from jproperties import Properties
+import csvfile
+import add_user
+import delete_user
configs = Properties()
with open("user.properties", "rb") as read_prop:
configs.load(read_prop)
-
+user_list = helper.read_json()
api_token = str(configs.get("api_token").data)
bot = telebot.TeleBot(api_token)
@@ -65,7 +75,6 @@ def listener(user_requests):
except Exception:
pass
-
bot.set_update_listener(listener)
@@ -80,7 +89,6 @@ def help(m):
commands = helper.getCommands()
for c in commands:
message += "/" + c + ", "
- # message += commands[c] + "\n\n"
message += "\nUse /menu for detailed instructions about these commands."
bot.send_message(chat_id, message)
@@ -96,9 +104,10 @@ def faq(m):
('"What does this bot do?"\n'
">> DollarBot lets you manage your expenses so you can always stay on top of them! \n\n"
'"How can I add an epxense?" \n'
- ">> Type /add, then select a category to type the expense. \n\n"
+ ">> Type /add_category, then add a category for the expense. \n\n"
+ ">> Type /add_category, then select a category to type the expense. \n\n"
'"Can I see history of my expenses?" \n'
- ">> Yes! Use /display to get a graphical display, or /history to view detailed summary.\n\n"
+ ">> Yes! Use /display to get a graphical display, or/history to view detailed summary.\n\n"
'"I added an incorrect expense. How can I edit it?"\n'
">> Use /edit command. \n\n"
'"Can I check if my expenses have exceeded budget?"\n'
@@ -115,9 +124,14 @@ def start_and_menu_command(m):
bot offers and the corresponding commands to be run from the Telegram UI to use these features.
Commands used to run this: commands=['start', 'menu']
"""
- helper.read_json()
global user_list
+ user_list = helper.read_json()
chat_id = m.chat.id
+ print(user_list)
+ if str(chat_id) not in user_list:
+ user_list[str(chat_id)] = helper.createNewUserRecord(m)
+
+
# print('receieved start or menu command.')
# text_into = "Welcome to the Dollar Bot!"
@@ -152,6 +166,24 @@ def command_add(message):
add.run(message, bot)
+@bot.message_handler(commands=["add_user"])
+def command_add_user(message):
+ add_user.register_people(message,bot,user_list)
+
+@bot.message_handler(commands=["delete_user"])
+def command_delete_user(message):
+ # Call the delete_user function from the delete_user module
+ registered_users=user_list[str(message.chat.id)]["users"]
+ delete_user.delete_user(message, bot, user_list)
+
+@bot.message_handler(commands=["add_category"])
+def command_add_category(message):
+ """
+ command_add(message) Takes 1 argument message which contains the message from
+ the user along with the chat ID of the user chat. It then calls add.py to run to execute
+ the add functionality. Commands used to run this: commands=['add']
+ """
+ add_category.run(message, bot)
# function to fetch expenditure history of the user
@@ -165,6 +197,15 @@ def command_pdf(message):
pdf.run(message, bot)
+@bot.message_handler(commands=["csv"])
+def command_csv(message):
+ """
+ command_history(message): Takes 1 argument message which contains the message from
+ the user along with the chat ID of the user chat. It then calls csv.py to run to execute
+ the add functionality. Commands used to run this: commands=['csv']
+ """
+ csvfile.run(message, bot)
+
# function to fetch expenditure history of the user
@bot.message_handler(commands=["history"])
def command_history(message):
@@ -192,7 +233,8 @@ def command_edit(message):
def command_display(message):
"""
command_display(message): Takes 1 argument message which contains the message from the user
- along with the chat ID of the user chat. It then calls display.py to run to execute the add functionality.
+ along with the chat ID of the user chat. It then calls display.py to run to execute
+ the add functionality.
Commands used to run this: commands=['display']
"""
display.run(message, bot)
@@ -208,24 +250,40 @@ def command_estimate(message):
@bot.message_handler(commands=["delete"])
def command_delete(message):
"""
- command_delete(message): Takes 1 argument message which contains the message from the user
- along with the chat ID of the user chat. It then calls delete.py to run to execute the add functionality.
+ command_delete(message): Takes 1 argument message which contains the
+ message from the user along with the chat ID of the user chat. It then
+ calls delete.py to run to execute the add functionality.
Commands used to run this: commands=['display']
"""
delete.run(message, bot)
+# handles "/delete_expense" command
+@bot.message_handler(commands=["delete_expense"])
+def command_delete(message):
+ """
+ command_delete(message): Takes 1 argument message which contains the
+ message from the user along with the chat ID of the user chat. It then
+ calls delete_expense.py to run to execute the add functionality.
+ Commands used to run this: commands=['display']
+ """
+ delete_expense.run(message, bot)
+
@bot.message_handler(commands=["budget"])
def command_budget(message):
budget.run(message, bot)
+@bot.message_handler(commands=["send_mail"])
+def command_send_mail(message):
+ send_mail.run(message, bot)
+
# not used
def addUserHistory(chat_id, user_record):
global user_list
- if not (str(chat_id) in user_list):
+ if not str(chat_id) in user_list:
user_list[str(chat_id)] = []
user_list[str(chat_id)].append(user_record)
return user_list
diff --git a/code/csvfile.py b/code/csvfile.py
new file mode 100644
index 000000000..1722eaf48
--- /dev/null
+++ b/code/csvfile.py
@@ -0,0 +1,59 @@
+import helper
+import logging
+from matplotlib import pyplot as plt
+from telebot import types
+import csv
+
+# === Documentation of pdf.py ===
+
+
+def run(message, bot):
+ try:
+ user_list=helper.read_json()
+ chat_id = message.chat.id
+ user_history = helper.getUserHistory(chat_id)
+ print('User-history--> ',user_history)
+ if user_history != None:
+ data = user_list[str(chat_id)]['csv_data']
+ message = "Alright. I just created a csv file of your expense history!"
+ bot.send_message(chat_id, message)
+ csv_file = 'expense_report.csv'
+ # Open the CSV file for writing
+ with open(csv_file, 'w', newline='') as file:
+ writer = csv.writer(file)
+
+ # Write the header row
+ writer.writerow(['Date', 'Category', 'Amount', 'Payer', 'Participants'])
+
+ # Write the data from the list
+ for item in data:
+ parts = item.split(',')
+ writer.writerow(parts)
+ bot.send_document(chat_id, open("expense_report.csv", "rb"))
+ print("CSV generated successfully.")
+ #issue 15 - modified the format of pdf document - start
+
+
+ #Issue 3 - added the else condition - start
+ else:
+ message = "Looks like you have not entered any data yet. Please enter some data and then try creating a pdf."
+ bot.send_message(chat_id, message)
+
+ display_text = ""
+ commands = helper.getCommands()
+ for (
+ c
+ ) in (
+ commands
+ ): # generate help text out of the commands dictionary defined at the top
+ display_text += "/" + c + ": "
+ display_text += commands[c] + "\n"
+ bot.send_message(chat_id, "Please select a menu option from below:")
+ bot.send_message(chat_id, display_text)
+ #Issue 3 - added the else condition - end
+
+ except Exception as e:
+ logging.exception(str(e))
+ bot.send_message(message, "Oops!" + str(e))
+
+
diff --git a/code/delete.py b/code/delete.py
index 6b7f9f2c1..e8455542f 100644
--- a/code/delete.py
+++ b/code/delete.py
@@ -12,12 +12,11 @@ def run(message, bot):
data saved in myDollarBot i.e their chat ID has been logged before, run calls the deleteHistory(chat_id):
to remove it. Then it ensures this removal is saved in the datastore.
"""
- global user_list
chat_id = message.chat.id
delete_history_text = ""
user_list = helper.read_json()
if str(chat_id) in user_list:
- helper.write_json(deleteHistory(chat_id))
+ helper.write_json(deleteHistory(chat_id,user_list))
delete_history_text = "History has been deleted!"
else:
delete_history_text = "No records there to be deleted. Start adding your expenses to keep track of your spendings!"
@@ -25,12 +24,11 @@ def run(message, bot):
# function to delete a record
-def deleteHistory(chat_id):
+def deleteHistory(chat_id,user_list):
"""
deleteHistory(chat_id): It takes 1 argument for processing - chat_id which is the
chat_id of the user whose data is to deleted from the user list. It removes this entry from the user list.
"""
- global user_list
if str(chat_id) in user_list:
del user_list[str(chat_id)]
return user_list
diff --git a/code/delete_expense.py b/code/delete_expense.py
new file mode 100644
index 000000000..5e1528fca
--- /dev/null
+++ b/code/delete_expense.py
@@ -0,0 +1,114 @@
+import helper
+from telebot import types
+import history
+# === Documentation of delete_expense.py ===
+
+def run(m, bot):
+ """
+ run(message, bot): This is the main function used to implement the delete feature.
+ It takes 2 arguments for processing - message which is the message from the user, and
+ bot which is the telegram bot object from the main code.py function. It gets the details
+ for the expense to be edited from here and passes control onto edit2(m, bot): for further processing.
+ """
+ chat_id = m.chat.id
+ markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
+ markup.row_width = 2
+ for c in helper.getUserHistory(chat_id):
+ expense_data = c.split(",")
+ str_date = "Date=" + expense_data[0]
+ str_category = ",\t\tCategory=" + expense_data[1]
+ str_amount = ",\t\tAmount=$" + expense_data[2]
+ markup.add(str_date + str_category + str_amount)
+ info = bot.reply_to(m, "Select expense to be deleted:", reply_markup=markup)
+ bot.register_next_step_handler(info, select_category_to_be_deleted, bot)
+
+def select_category_to_be_deleted(m, bot):
+ info = m.text
+ markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
+ markup.row_width = 2
+ choice = bot.reply_to(m, "Are you sure you want to delete? Y/N")
+ bot.register_next_step_handler(choice, delete_selected_data, bot, info)
+
+
+
+def delete_selected_data(message, bot, selected_data):
+ chat_id = message.chat.id
+ user_history = helper.getUserHistory(chat_id)
+
+ # Check if the user has selected any data
+ if not selected_data:
+ bot.send_message(chat_id, "No data selected for deletion.")
+ return
+
+ if str(message.text) == "Y" or str(message.text) == "y":
+ # Initialize a list to keep track of the deleted records
+ deleted_records = []
+
+ components = selected_data.split(',')
+ formatted_data = []
+
+ for component in components:
+ key, value = component.split('=')
+ value = value.strip()
+
+ # Check if the component is the "Amount" and remove the '$' sign
+ if key.strip() == "Amount" and value.startswith("$"):
+ value = value[1:]
+
+ formatted_data.append(value)
+
+ formatted_string = ','.join(formatted_data)
+
+ # Compare each item in user_history with selected_data
+ for expense_data in user_history:
+ if formatted_string in expense_data:
+ user_history.remove(expense_data)
+ deleted_records.append(expense_data)
+
+ # Update the user's history
+ user_list = helper.read_json()
+ user_list[str(chat_id)]["data"] = user_history
+ helper.write_json(user_list)
+
+ # Provide feedback to the user about the deleted records
+ if deleted_records:
+ bot.send_message(chat_id, "The following record has been deleted:")
+ # Create a tabular representation of the data
+ tabular_data = "```"
+ tabular_data += "+-------------------+-------------------+-------------+\n"
+ tabular_data += "| DATE | CATEGORY | AMOUNT |\n"
+ tabular_data += "+-------------------+-------------------+-------------+\n"
+
+ for line in deleted_records:
+ rec = line.split(",") # Assuming data is comma-separated
+ if len(rec) == 3:
+ tabular_data += "| {:<15} | {:<17} | {:<11} |\n".format(rec[0], rec[1], rec[2])
+
+ tabular_data += "+-------------------+-------------------+-------------+"
+ tabular_data += "```"
+
+ # Send the tabular data as a Markdown-formatted message
+ bot.send_message(chat_id, tabular_data, parse_mode="Markdown")
+
+ msg = bot.send_message(chat_id, "Do you want to see the updated expense history? Y/N")
+ bot.register_next_step_handler(msg, show_updated_expense_history, bot)
+
+ else:
+ bot.send_message(chat_id, "No matching records found for deletion.")
+ else:
+ bot.send_message(chat_id, "No data deleted.")
+
+def show_updated_expense_history(message, bot):
+ if str(message.text) == "Y" or str(message.text) == "y":
+ history.run(message, bot)
+
+# function to delete a record
+def deleteHistory(chat_id):
+ """
+ deleteHistory(chat_id): It takes 1 argument for processing - chat_id which is the
+ chat_id of the user whose data is to deleted from the user list. It removes this entry from the user list.
+ """
+ global user_list
+ if str(chat_id) in user_list:
+ del user_list[str(chat_id)]
+ return user_list
diff --git a/code/delete_user.py b/code/delete_user.py
new file mode 100644
index 000000000..d3db0ef6a
--- /dev/null
+++ b/code/delete_user.py
@@ -0,0 +1,50 @@
+import helper
+from telebot import types
+
+# Initialize a dictionary to store registered users
+registered_users = {}
+
+def delete_user(message, bot, user_list):
+ chat_id = message.chat.id
+ user_dict = user_list.get(str(chat_id), {})
+
+ if not user_dict or "users" not in user_dict:
+ bot.send_message(chat_id, "No users are registered for deletion.")
+ return
+
+ # Get the list of all users from user_list
+ all_users = user_dict.get("users", [])
+
+ # Create a custom keyboard to let the user choose which user to delete
+ markup = types.ReplyKeyboardMarkup(one_time_keyboard=True, resize_keyboard=True)
+ for user in all_users:
+ markup.add(user)
+
+ msg = bot.send_message(chat_id, "Select the user you want to delete:", reply_markup=markup)
+ bot.register_next_step_handler(msg, confirm_delete, bot, user_list)
+
+def confirm_delete(message, bot, user_list):
+ chat_id = message.chat.id
+ user_name = message.text
+
+ user_dict = user_list.get(str(chat_id), {})
+
+ if "users" in user_dict and user_name in user_dict["users"]:
+ user_dict["users"].remove(user_name)
+ user_dict["owed"].pop(user_name, None)
+ user_dict["owing"].pop(user_name, None)
+
+ helper.write_json(user_list)
+
+ bot.send_message(chat_id, f"{user_name} has been deleted successfully.")
+
+ if not user_dict["users"]:
+ bot.send_message(chat_id, "No users are registered after deletion.")
+ else:
+ bot.send_message(chat_id, "Updated list of registered users:\n" + '\n '.join(user_dict["users"]))
+ else:
+ bot.send_message(chat_id, f"{user_name} is not registered.")
+
+ # Remove the custom keyboard
+ # markup = types.ReplyKeyboardRemove(selective=False)
+ # bot.send_message(chat_id, "Keyboard hidden. You can now use other commands.", reply_markup=markup)
diff --git a/code/display.py b/code/display.py
index 0d7715980..e30241835 100644
--- a/code/display.py
+++ b/code/display.py
@@ -14,7 +14,7 @@ def run(message, bot):
It takes 2 arguments for processing - message which is the message from the user, and bot
which is the telegram bot object from the main code.py function.
"""
- helper.read_json()
+ user_list=helper.read_json()
chat_id = message.chat.id
history = helper.getUserHistory(chat_id)
if history is None:
@@ -23,17 +23,56 @@ def run(message, bot):
)
else:
markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
- markup.row_width = 2
- for mode in helper.getSpendDisplayOptions():
- markup.add(mode)
- # markup.add('Day', 'Month')
- msg = bot.reply_to(
- message,
- "Please select a category to see the total expense",
- reply_markup=markup,
- )
- bot.register_next_step_handler(msg, display_total, bot)
+ markup.add("Display all expenses")
+ markup.add("Display owings")
+ m = bot.send_message(chat_id, "Select what to display",reply_markup=markup)
+ bot.register_next_step_handler(m, display_choice, bot,user_list,chat_id)
+
+
+def display_choice(message,bot,user_list,chat_id):
+ chat_id = message.chat.id
+ choice = message.text
+ if choice == 'Display all expenses':
+ display_expenses(message,bot)
+ elif choice =='Display owings':
+ display_owings(message,bot,user_list,chat_id)
+ else:
+ m = bot.send_message(chat_id, "Select correct choice")
+ bot.register_next_step_handler(m, run, bot)
+
+def display_owings(message,bot,user_list,chat_id):
+ markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
+ markup.row_width = len(user_list[str(chat_id)]["users"])
+ for c in user_list[str(chat_id)]["users"]:
+ markup.add(c)
+ m = bot.send_message(chat_id, "Select user who's owings you want to display",reply_markup=markup)
+ bot.register_next_step_handler(m, select_user, bot,user_list,chat_id)
+
+def select_user(message,bot,user_list,chat_id):
+ chat_id = message.chat.id
+ user = message.text
+ owing_dictionary = helper.calculate_owing(user_list,chat_id)
+ final_string = ''
+ for owed in owing_dictionary[user]["owes"]:
+ final_string+=str("\n "+owed)
+ for owing in owing_dictionary[user]["owing"]:
+ final_string+=str("\n "+owing)
+ if final_string == '':
+ final_string = str(user)+' owes or is owed nothing'
+ m = bot.send_message(chat_id, final_string)
+def display_expenses(message, bot):
+ markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
+ markup.row_width = 2
+ for mode in helper.getSpendDisplayOptions():
+ markup.add(mode)
+ # markup.add('Day', 'Month')
+ msg = bot.reply_to(
+ message,
+ "Please select a category to see the total expense",
+ reply_markup=markup,
+ )
+ bot.register_next_step_handler(msg, display_total, bot)
def display_total(message, bot):
"""
@@ -77,21 +116,38 @@ def display_total(message, bot):
value for index, value in enumerate(history) if str(query) in value
]
total_text = calculate_spendings(queryResult)
+ print("###########",total_text)
monthly_budget = helper.getCategoryBudget(chat_id)
- print("Print Total Spending", total_text)
- print("Print monthly budget", monthly_budget)
+ if monthly_budget == None:
+ message = "Looks like you have not entered any category-wise budget yet. Please enter your budget and then try to display the expenses."
+ bot.send_message(chat_id, message)
- spending_text = ""
- if len(total_text) == 0:
- spending_text = "You have no spendings for {}!".format(DayWeekMonth)
- bot.send_message(chat_id, spending_text)
+ display_text = ""
+ commands = helper.getCommands()
+ for (
+ c
+ ) in (
+ commands
+ ): # generate help text out of the commands dictionary defined at the top
+ display_text += "/" + c + ": "
+ display_text += commands[c] + "\n"
+ bot.send_message(chat_id, "Please select a menu option from below:")
+ bot.send_message(chat_id, display_text)
else:
- spending_text = "Here are your total spendings {}:\nCATEGORIES,AMOUNT \n----------------------\n{}".format(
- DayWeekMonth.lower(), total_text
- )
- graphing.visualize(total_text, monthly_budget)
- bot.send_photo(chat_id, photo=open("expenditure.png", "rb"))
- # os.remove('expenditure.png')
+ print("Print Total Spending", total_text)
+ print("Print monthly budget", monthly_budget)
+
+ spending_text = ""
+ if len(total_text) == 0:
+ spending_text = "You have no spendings for {}!".format(DayWeekMonth)
+ bot.send_message(chat_id, spending_text)
+ else:
+ spending_text = "Here are your total spendings {}:\nCATEGORIES,AMOUNT \n----------------------\n{}".format(
+ DayWeekMonth.lower(), total_text
+ )
+ graphing.visualize(total_text, monthly_budget)
+ bot.send_photo(chat_id, photo=open("expenditure.png", "rb"))
+ # os.remove('expenditure.png')
except Exception as e:
logging.exception(str(e))
bot.reply_to(message, str(e))
@@ -104,7 +160,7 @@ def calculate_spendings(queryResult):
It parses the query result and turns it into a form suitable for display on the UI by the user.
"""
total_dict = {}
-
+ print("!!!!!!",queryResult)
for row in queryResult:
# date,cat,money
s = row.split(",")
diff --git a/code/graphing.py b/code/graphing.py
index 8e08ce69b..cb4d86ac1 100644
--- a/code/graphing.py
+++ b/code/graphing.py
@@ -39,8 +39,6 @@ def visualize(total_text, monthly_budget):
"""
n1 = len(monthly_budget)
r1 = np.arange(n1)
- print(n1)
- print(r1)
width = 0.45
total_text_split = [line for line in total_text.split("\n") if line.strip() != ""]
monthly_budget_str = ""
@@ -49,11 +47,6 @@ def visualize(total_text, monthly_budget):
monthly_budget_split = [
line for line in monthly_budget_str.split("\n") if line.strip() != ""
]
- categ_val = {}
- for i in total_text_split:
- a = i.split(" ")
- a[1] = a[1].replace("$", "")
- categ_val[a[0]] = float(a[1])
monthly_budget_categ_val = {}
for j in monthly_budget_split:
@@ -61,11 +54,16 @@ def visualize(total_text, monthly_budget):
x[1] = x[1].replace("$", "")
monthly_budget_categ_val[x[0]] = float(x[1])
+ categ_val = {key: 0 for key in monthly_budget_categ_val}
+ for i in total_text_split:
+ a = i.split(" ")
+ a[1] = a[1].replace("$", "")
+ categ_val[a[0]] = float(a[1])
+
x = list(categ_val.keys())
y = list(categ_val.values())
n2 = len(x)
r2 = np.arange(n2)
-
plt.bar(r2, categ_val.values(), width=width, label="your spendings")
plt.bar(
r1 + width, monthly_budget_categ_val.values(), width=width, label="your budget"
diff --git a/code/helper.py b/code/helper.py
index 70c8c48d4..4bee3d497 100644
--- a/code/helper.py
+++ b/code/helper.py
@@ -1,10 +1,13 @@
+'''
+File which provides all supporting data
+'''
+
import re
import json
import os
from datetime import datetime
from notify import notify
-
spend_categories = [
"Food",
"Groceries",
@@ -18,25 +21,42 @@
spend_estimate_option = ["Next day", "Next month"]
update_options = {"continue": "Continue", "exit": "Exit"}
-budget_options = {"update": "Add/Update", "view": "View", "delete": "Delete"}
+budget_options = {"add":"Add","update": "Update", "view": "View", "delete": "Delete"}
budget_types = {"overall": "Overall Budget", "category": "Category-Wise Budget"}
-data_format = {"data": [], "budget": {"overall": None, "category": None}}
+data_format = {"users":[],"owed":{},"owing":{},"data": [],"csv_data":[],
+ "budget": {"overall": '0', "category": {"Food": '0',
+ "Groceries": '0',
+ "Utilities": '0',
+ "Transport": '0',
+ "Shopping": '0',
+ "Miscellaneous": '0'}
+ }
+}
# set of implemented commands and their description
commands = {
"help": "Display the list of commands.",
"pdf": "Save history as PDF.",
+ "csv": "Save history as a cv file.",
+ "add_user": "Add users to expense tracker",
+ "delete_user":"Delete user from the registered users",
"add": "This option is for adding your expenses \
\n 1. It will give you the list of categories to choose from. \
\n 2. You will be prompted to enter the amount corresponding to your spending \
\n 3.The message will be prompted to notify the addition of your expense with the amount,date, time and category ",
+ "add_category": "This option is for adding new category \
+ \n 1. You will be prompted to enter a new category \
+ \n 2.The message will be prompted to notify the addition of your category ",
+
"display": "This option gives user a graphical representation(bar graph) of their expenditures \
\n You will get an option to choose from day or month for better analysis of the expenses.",
"estimate": "This option gives you the estimate of expenditure for the next day/month. It calcuates based on your recorded spendings",
"history": "This option is to give you the detailed summary of your expenditure with Date, time ,category and amount. A quick lookup into your spendings",
"delete": "This option is to Clear/Erase all your records",
+ "delete_expense": "This option is to Clear/Erase individual record from expense history records.",
+ "send_mail": "This option is to send mail of calculate owings",
"edit": "This option helps you to go back and correct/update the missing details \
\n 1. It will give you the list of your expenses you wish to edit \
\n 2. It will let you change the specific field based on your requirements like amount/date/category",
@@ -109,7 +129,6 @@ def getUserHistory(chat_id):
return data["data"]
return None
-
def getUserData(chat_id):
user_list = read_json()
if user_list is None:
@@ -124,22 +143,32 @@ def throw_exception(e, message, bot, logging):
bot.reply_to(message, "Oh no! " + str(e))
-def createNewUserRecord():
- return data_format
+def createNewUserRecord(message):
+ user_lst = data_format
+ if len(user_lst["users"]) == 0:
+ user_lst["users"].insert(0,message.from_user.first_name)
+ user_lst["owed"][message.from_user.first_name] = 0
+ user_lst["owing"][message.from_user.first_name] = {}
+ return user_lst
def getOverallBudget(chatId):
data = getUserData(chatId)
if data is None:
return None
- return data["budget"]["overall"]
+ if 'budget' in data.keys():
+ return data["budget"]["overall"]
+ return None
def getCategoryBudget(chatId):
data = getUserData(chatId)
if data is None:
return None
- return data["budget"]["category"]
+ if 'budget' in data.keys():
+ return data["budget"]["category"]
+ return None
+
def getCategoryBudgetByCategory(chatId, cat):
@@ -209,6 +238,23 @@ def calculate_total_spendings(queryResult):
total = total + float(s[2])
return total
+def calculate_owing(user_list,chat_id):
+ owing_dict = {}
+ users = user_list[str(chat_id)]["users"]
+ for user in users:
+ owing_dict[user] = {"owes" : [], "owing":[]}
+ for k,v in user_list[str(chat_id)]["owing"][user].items():
+ if k in owing_dict.keys():
+ owing_dict[k]["owing"].append(str(user)+' owes '+str(k)+" an amout of "+"{:.2f}".format(v))
+ owing_dict[user]["owes"].append(str(k)+' is owing from '+str(user)+" an amout of "+"{:.2f}".format(v))
+
+ else:
+ owing_dict[k] ={"owes" :[str(k)+' is owing from '+str(user)+" an amout of "+"{:.2f}".format(v)],"owing" :[str(user)+' owes '+str(k)+" an amout of "+"{:.2f}".format(v)]}
+
+ return owing_dict
+
+
+
def display_remaining_category_budget(message, bot, cat):
chat_id = message.chat.id
diff --git a/code/history.py b/code/history.py
index c52978512..e32158a5c 100644
--- a/code/history.py
+++ b/code/history.py
@@ -1,6 +1,7 @@
import helper
import logging
-
+import csv
+from io import StringIO
# === Documentation of history.py ===
@@ -12,20 +13,36 @@ def run(message, bot):
historical data and based on whether there is data available, it either prints an error message or
displays the user's historical data.
"""
+
try:
helper.read_json()
chat_id = message.chat.id
user_history = helper.getUserHistory(chat_id)
- spend_total_str = ""
+
if user_history is None:
raise Exception("Sorry! No spending records found!")
- spend_total_str = "Here is your spending history : \nDATE, CATEGORY, AMOUNT\n----------------------\n"
+
if len(user_history) == 0:
- spend_total_str = "Sorry! No spending records found!"
+ bot.send_message(chat_id, "Sorry! No spending records found!")
else:
- for rec in user_history:
- spend_total_str += str(rec) + "\n"
- bot.send_message(chat_id, spend_total_str)
+ # Create a tabular representation of the data
+ tabular_data = "```"
+ tabular_data += "+-------------------+-------------------+-------------+\n"
+ tabular_data += "| DATE | CATEGORY | AMOUNT |\n"
+ tabular_data += "+-------------------+-------------------+-------------+\n"
+
+ for line in user_history:
+ rec = line.split(",") # Assuming data is comma-separated
+ if len(rec) == 3:
+ tabular_data += "| {:<15} | {:<17} | {:<11} |\n".format(rec[0], rec[1], rec[2])
+
+ tabular_data += "+-------------------+-------------------+-------------+"
+ tabular_data += "```"
+
+ # Send the tabular data as a Markdown-formatted message
+ bot.send_message(chat_id, tabular_data, parse_mode="Markdown")
+
except Exception as e:
logging.exception(str(e))
- bot.reply_to(message, "Oops!" + str(e))
+ bot.reply_to(message, "Oops! " + str(e))
+
diff --git a/code/pdf.py b/code/pdf.py
index 6996ad32f..da4ff5d98 100644
--- a/code/pdf.py
+++ b/code/pdf.py
@@ -1,10 +1,11 @@
import helper
import logging
from matplotlib import pyplot as plt
+from telebot import types
+from tabulate import tabulate
+from fpdf import FPDF
# === Documentation of pdf.py ===
-
-
def run(message, bot):
"""
run(message, bot): This is the main function used to implement the pdf save feature.
@@ -12,42 +13,153 @@ def run(message, bot):
try:
helper.read_json()
chat_id = message.chat.id
+
+ user_list = helper.read_json()
+ #print('User-history--> ',user_history)
+ print('User_list', user_list)
+
+ markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
+ #markup.row_width = 2
+ markup.add("PDF for Total Expenses - Category wise", "PDF showing who owes whom how much")
+ msg = bot.send_message(chat_id, "Which kind of PDF do you want to generate?", reply_markup=markup)
+
user_history = helper.getUserHistory(chat_id)
- message = "Alright. I just created a pdf of your expense history!"
- bot.send_message(chat_id, message)
- fig = plt.figure()
- ax = fig.add_subplot(1, 1, 1)
- top = 0.8
- if len(user_history) == 0:
- plt.text(
- 0.1,
- top,
- "No record found!",
- horizontalalignment="left",
- verticalalignment="center",
- transform=ax.transAxes,
- fontsize=20,
- )
- for rec in user_history:
- date, category, amount = rec.split(",")
- date, time = date.split(" ")
- print(date, category, amount)
- rec_str = f"{amount}$ {category} expense on {date} at {time}"
- plt.text(
- 0,
- top,
- rec_str,
- horizontalalignment="left",
- verticalalignment="center",
- transform=ax.transAxes,
- fontsize=14,
- bbox=dict(facecolor="red", alpha=0.3),
- )
- top -= 0.15
- plt.axis("off")
- plt.savefig("expense_history.pdf")
- plt.close()
- bot.send_document(chat_id, open("expense_history.pdf", "rb"))
+
+ bot.register_next_step_handler(msg, pdfGeneration, bot,user_list, user_history)
+
except Exception as e:
logging.exception(str(e))
bot.reply_to(message, "Oops!" + str(e))
+
+def pdfGeneration(message, bot, user_list, user_history):
+ chat_id = message.chat.id
+ choice = message.text
+
+ if choice == 'PDF for Total Expenses - Category wise':
+ #Issue 3 - added the if condition - start
+ if user_history != None:
+ #Issue 3 - added the if condition - end
+ message = "Alright. I just created a pdf of your expense history!"
+ bot.send_message(chat_id, message)
+ fig = plt.figure()
+ ax = fig.add_subplot(1, 1, 1)
+ top = 0.8
+ if len(user_history) == 0:
+ plt.text(
+ 0.1,
+ top,
+ "No record found!",
+ horizontalalignment="left",
+ verticalalignment="center",
+ transform=ax.transAxes,
+ fontsize=20,
+ )
+
+ #issue 15 - modified the format of pdf document - start
+ table_data = [entry.split(',') for entry in user_history]
+
+ # Create a PDF document
+ pdf = FPDF()
+ pdf.add_page()
+
+ # Set font
+ pdf.set_font("helvetica", size=12)
+
+ # Define the table columns
+ columns = ["Date & Time", "Category", "Amount"]
+
+ # Create a table and set its properties
+ pdf.set_fill_color(135, 206, 235) # Light blue
+ pdf.set_font(family="helvetica",style="B")
+ pdf.cell(0, 10, "Expense Report", ln=1, align="C", fill=True)
+ pdf.set_fill_color(255, 255, 255) # White
+ pdf.ln()
+ pdf.ln()
+ # Add table headers
+ pdf.set_font("helvetica", size=12, style="B")
+ for col in columns:
+ pdf.cell(64, 10, col, border=1, align="C", fill=True)
+ pdf.ln()
+
+ # Add table data
+ pdf.set_font("helvetica", size=10)
+ for row in table_data:
+ for item in row:
+ pdf.cell(64, 10, item, border=1, align="C", fill=True)
+ pdf.ln()
+
+ # Save the PDF
+ pdf.output("expense_report.pdf")
+ bot.send_document(chat_id, open("expense_report.pdf", "rb"))
+ print("PDF generated successfully.")
+ else:
+ message = "Looks like you have not entered any data yet. Please enter some data and then try creating a pdf."
+ bot.send_message(chat_id, message)
+
+ display_text = ""
+ commands = helper.getCommands()
+ for (
+ c
+ ) in (
+ commands
+ ): # generate help text out of the commands dictionary defined at the top
+ display_text += "/" + c + ": "
+ display_text += commands[c] + "\n"
+ bot.send_message(chat_id, "Please select a menu option from below:")
+ bot.send_message(chat_id, display_text)
+ elif choice == 'PDF showing who owes whom how much':
+ message = "Alright. I just created a pdf of your expense history!"
+ bot.send_message(chat_id, message)
+
+ if user_history != None:
+ pdf = FPDF()
+ pdf.add_page()
+
+ pdf.set_font("Arial", size=12)
+ pdf.set_fill_color(135, 206, 235) # Light blue
+ pdf.set_font(family="Arial",style="B")
+ pdf.cell(0, 10, "Expense Report", ln=1, align="C", fill=True)
+ pdf.set_fill_color(255, 255, 255) # White
+ pdf.ln()
+ pdf.ln()
+
+ pdf.set_font("helvetica", size=12, style="B")
+ pdf.cell(50, 10, "User", border=1, align="C", fill=True)
+ pdf.cell(50, 10, "Gets back", border=1, align="C", fill=True)
+ pdf.cell(50, 10, "Gives to", border=1, align="C", fill=True)
+ pdf.cell(40, 10, "Gives Amount", border=1, align="C", fill=True)
+ #pdf.cell(40, 10, "Data", border=1)
+ pdf.ln()
+
+ pdf.set_font("helvetica", size=12)
+ for user, details in user_list.items():
+ for i, user_name in enumerate(details["users"]):
+ #amounts = ''
+ pdf.cell(50, 10, user_name, border=1, align="C", fill=True)
+ pdf.cell(50, 10, str(round(details["owed"][user_name], 2)), border=1, align="C", fill=True)
+ pdf.cell(50, 10, ', '.join(details["owing"][user_name].keys()), border=1, align="C", fill=True)
+ if details["owing"][user_name].values() != []:
+ pdf.cell(40, 10, ', '.join([str(round(x,2)) for x in details["owing"][user_name].values()]), border=1, align="C", fill=True)
+ else:
+ pdf.cell(40, 10, "None", border=1, align="C", fill=True)
+ pdf.ln()
+
+ pdf.output("OwingTable.pdf")
+ bot.send_document(chat_id, open("OwingTable.pdf", "rb"))
+ print("PDF table created successfully.")
+
+ else:
+ message = "Looks like you have not entered any data yet. Please enter some data and then try creating a pdf."
+ bot.send_message(chat_id, message)
+
+ display_text = ""
+ commands = helper.getCommands()
+ for (
+ c
+ ) in (
+ commands
+ ): # generate help text out of the commands dictionary defined at the top
+ display_text += "/" + c + ": "
+ display_text += commands[c] + "\n"
+ bot.send_message(chat_id, "Please select a menu option from below:")
+ bot.send_message(chat_id, display_text)
\ No newline at end of file
diff --git a/code/send_mail.py b/code/send_mail.py
new file mode 100644
index 000000000..873fe6f9c
--- /dev/null
+++ b/code/send_mail.py
@@ -0,0 +1,129 @@
+import helper
+import smtplib
+from email.mime.text import MIMEText
+from email.mime.multipart import MIMEMultipart
+from fpdf import FPDF
+
+user_emails = {}
+
+# === Documentation of add.py ===
+def run(message, bot):
+ helper.read_json()
+ chat_id = message.chat.id
+ message1 = bot.send_message(chat_id, "Please enter the email address")
+ bot.register_next_step_handler(message1, add_emails, bot)
+
+def add_emails(message, bot):
+ """
+ add_emails(message, bot):
+ Takes 2 arguments - message (a message received from the user) and bot (the chatbot instance).
+ This function extracts the email from the user's message and associates it with the user's chat ID in the user_emails dictionary.
+ It can also perform email validation. If the email is invalid, it sends a message to the user to enter a valid email.
+ Finally, the user is notified that their email has been recorded and is asked if they want to send an email to the provided address.
+ """
+ chat_id = message.chat.id
+ email = message.text
+ # Assuming you want to store the email address in the user_emails dictionary
+ user_emails[chat_id] = email
+
+ # You can also validate the email address if needed
+ if not is_valid_email(email):
+ bot.send_message(chat_id, "Invalid email address. Please enter a valid email.")
+ return
+
+ # Notify the user that their email address has been recorded
+ choice = bot.send_message(chat_id, f"Thank you for providing your email. Do you want to send email to {email}? Y/N")
+ bot.register_next_step_handler(choice, send_email, bot)
+
+# Example of a basic email validation function (you can expand this)
+def is_valid_email(email):
+ """
+ is_valid_email(email):
+ Takes one argument - email (the email address to be validated).
+ This function checks if the provided email address is in a valid format using a regular expression pattern.
+ If the email is in a valid format, it returns True; otherwise, it returns False.
+ """
+ import re
+ email_pattern = r'^\S+@\S+\.\S+$'
+ return re.match(email_pattern, email) is not None
+
+def send_email(choice, bot):
+ """
+ send_email(choice, bot):
+ Takes two arguments - choice (user's choice of sending an email) and bot (the chatbot instance).
+ If the user's choice is 'Y' or 'y', this function sets up a Gmail SMTP connection, composes and sends emails to all users stored in the user_emails dictionary.
+ It uses a Gmail account for sending emails, and the email content is based on data obtained from the 'helper.read_json()' function.
+ After sending the emails, it closes the SMTP connection.
+ """
+ if str(choice.text) == "Y" or str(choice.text) == "y":
+ # Set up the Gmail API
+ smtp_server = 'smtp.gmail.com'
+ smtp_port = 587 # Port for TLS
+ smtp_username = 'csc510group32@gmail.com'
+ smtp_password = 'hqrx opxo lviu mubb'
+
+ # Create an SMTP connection
+ server = smtplib.SMTP(smtp_server, smtp_port)
+ server.starttls()
+ server.login(smtp_username, smtp_password)
+
+
+ # Compose and send emails to all users
+ for chat_id, email in user_emails.items():
+ # Create a MIME message with a subject
+ subject = "Calculated Owings"
+ message_body = format_text_data(helper.read_json())
+ message = MIMEMultipart()
+ message['From'] = smtp_username
+ message['To'] = email
+ message['Subject'] = subject
+ message.attach(MIMEText(message_body, 'plain'))
+
+ # Send the email
+ server.sendmail(smtp_username, email, message.as_string())
+
+ # Close the SMTP connection
+ server.quit()
+
+
+
+def format_text_data(user_list):
+ """
+ format_text_data(user_list):
+ Takes one argument - user_list (a dictionary containing details about owed and owing amounts among users).
+ This function formats the provided user_list data into a text representation with detailed information on who owes and is owed money.
+ The formatted text data is enclosed in triple backticks for use in Markdown or code block formatting.
+ The resulting text data is returned as a string.
+ """
+ text_data = "```\n"
+
+ for user, details in user_list.items():
+ for user_name in details["users"]:
+ gets_back_amount = round(details['owed'][user_name], 2)
+ text_data += f"{user_name} gets back {gets_back_amount} dollars.\n"
+
+ gives_to_list = list(details["owing"][user_name].keys())
+ gives_amount_list = list(details["owing"][user_name].values())
+
+ if not gives_to_list:
+ text_data += f"{user_name} gives to no one.\n"
+ else:
+ for i in range(len(gives_to_list)):
+ gives_to_entry = gives_to_list[i]
+ gives_amount_entry = round(gives_amount_list[i], 2)
+ text_data += f"{user_name} gives {gives_amount_entry} dollars to {gives_to_entry}.\n"
+
+ text_data += "```"
+ return text_data
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/Update_Version.pdf b/docs/Update_Version.pdf
new file mode 100644
index 000000000..cffa23386
Binary files /dev/null and b/docs/Update_Version.pdf differ
diff --git a/docs/add.md b/docs/add.md
index 118589551..2d6e62d50 100644
--- a/docs/add.md
+++ b/docs/add.md
@@ -1,5 +1,5 @@
-# About MyDollarBot's /add Feature
-This feature enables the user to add a new expense to their expense tracker.
+# About DollarSplitBot's /add Feature
+This part of a Telegram bot application helps users track their expenses and split costs with others.
Currently we have the following expense categories set by default:
- Food
@@ -12,41 +12,94 @@ Currently we have the following expense categories set by default:
The user can choose a category and add the amount they have spent to be stored in the expense tracker.
# Location of Code for this Feature
-The code that implements this feature can be found [here](https://github.com/sak007/MyDollarBot-BOTGo/blob/main/code/add.py)
+The code that implements this feature can be found [here](https://github.com/shonilbhide/dollar_bot/blob/main/code/add.py)
# Code Description
## Functions
1. run(message, bot):
-This is the main function used to implement the add feature. It pop ups a menu on the bot asking the user to choose their expense category, after which control is given to post_category_selection(message, bot) for further proccessing. It takes 2 arguments for processing - **message** which is the message from the user, and **bot** which is the telegram bot object from the main code.py function.
+This is the main function of the "add" feature. It prompts the user to select an expense category and then directs them to the `post_category_selection` function to enter the expense amount.
+ - Arguments: `message` (user's message) and `bot` (Telegram bot object).
+2. `select_user(message, bot, owed_by, user_list, paid_by)`:
+This function is used to select users who shared the expense. It allows users to add multiple participants for sharing an expense.
+ - Arguments: `message` (user's message), `bot` (Telegram bot object), `owed_by` (a list of users who owe), `user_list` (list of users), and `paid_by` (the user who paid initially).
-2. post_category_selection(message, bot):
- It takes 2 arguments for processing - **message** which is the message from the user, and **bot** which is the telegram bot object from the run(message, bot): function in the add.py file. It requests the user to enter the amount they have spent on the expense category chosen and then passes control to post_amount_input(message, bot): for further processing.
+3. `add_shared_user(message, bot, owed_by, user_list, paid_by)`:
+This function lets the user choose additional participants for sharing the expense and adds them to the list.
+ - Arguments: `message` (user's message), `bot` (Telegram bot object), `owed_by` (a list of users who owe), `user_list` (list of users), and `paid_by` (the user who paid initially).
-3. post_amount_input(message, bot):
- It takes 2 arguments for processing - **message** which is the message from the user, and **bot** which is the telegram bot object from the post_category_selection(message, bot): function in the add.py file. It takes the amount entered by the user, validates it with helper.validate() and then calls add_user_record to store it.
+4. `user_choice(message, bot, owed_by, user_list, paid_by)`:
+It handles the user's choice of adding more participants or proceeding to enter the expense category.
+ - Arguments: `message` (user's message), `bot` (Telegram bot object), `owed_by` (a list of users who owe), `user_list` (list of users), and `paid_by` (the user who paid initially).
-4. add_user_record(chat_id, record_to_be_added):
- Takes 2 arguments - **chat_id** or the chat_id of the user's chat, and **record_to_be_added** which is the expense record to be added to the store. It then stores this expense record in the store.
+5. `post_append_spend(message, bot, owed_by, user_list, paid_by)`:
+This function prompts the user to select an expense category for the shared expense.
+ - Arguments: `message` (user's message), `bot` (Telegram bot object), `owed_by` (a list of users who owe), `user_list` (list of users), and `paid_by` (the user who paid initially).
+
+6. `post_category_selection(message, bot, owed_by, paid_by, user_list)`:
+This function asks the user to enter the amount spent on the chosen expense category and then directs to the `post_amount_input` function.
+ - Arguments: `message` (user's message), `bot` (Telegram bot object), `owed_by` (a list of users who owe), `paid_by` (the user who paid initially), and `user_list` (list of users).
+
+7. `post_amount_input(message, bot, selected_category, owed_by, paid_by, user_list)`:
+It validates the amount entered by the user, then calls the `add_user_record` function to store the expense data.
+ - Arguments: `message` (user's message), `bot` (Telegram bot object), `selected_category` (the chosen expense category), `owed_by` (a list of users who owe), `paid_by` (the user who paid initially), and `user_list` (list of users).
+
+8. `add_user_record(user_list, message, chat_id, record_to_be_added, amount_value, owed_by, paid_by)`:
+This function stores the expense record in the user's data, updates who owes whom, and keeps track of the shared expense.
+ - Arguments: `user_list` (list of users and their expenses), `message` (user's message), `chat_id` (user's chat ID), `record_to_be_added` (expense record), `amount_value` (amount spent), `owed_by` (a list of users who owe), and `paid_by` (the user who paid initially).
+
+This code segment is part of a larger bot application, and it manages the addition of expenses and splitting costs among users. It helps users keep track of their spending and shared expenses with friends or groups.
# How to run this feature?
Once the project is running(please follow the instructions given in the main README.md for this), please type /add into the telegram bot.
Below you can see an example in text format:
-dollarbot, [19.10.21 21:14]
-[In reply to Sri Athithya Kruth]
+Rutuja Rashinkar, [19-10-2023 08:27 PM]
+/add
+
+My_MyDollarBot_bot, [19-10-2023 08:27 PM]
+Select who paid for the Expense
+
+Rutuja Rashinkar, [19-10-2023 08:27 PM]
+Rutuja Rashinkar
+
+My_MyDollarBot_bot, [19-10-2023 08:27 PM]
+Select who shares the Expense
+
+Rutuja Rashinkar, [19-10-2023 08:27 PM]
+B
+
+My_MyDollarBot_bot, [19-10-2023 08:27 PM]
+Do you want to add more user to share the expense? Y/N
+
+Rutuja Rashinkar, [19-10-2023 08:28 PM]
+Y
+
+My_MyDollarBot_bot, [19-10-2023 08:28 PM]
+Select who shares the Expense
+
+Rutuja Rashinkar, [19-10-2023 08:28 PM]
+C
+
+My_MyDollarBot_bot, [19-10-2023 08:28 PM]
+Do you want to add more user to share the expense? Y/N
+
+Rutuja Rashinkar, [19-10-2023 08:28 PM]
+N
+
+My_MyDollarBot_bot, [19-10-2023 08:28 PM]
+Select a category
+
+My_MyDollarBot_bot, [19-10-2023 08:28 PM]
Select Category
-Sri Athithya Kruth, [19.10.21 21:14]
-Food
+Rutuja Rashinkar, [19-10-2023 08:28 PM]
+Groceries
-dollarbot, [19.10.21 21:14]
-How much did you spend on Food?
+My_MyDollarBot_bot, [19-10-2023 08:28 PM]
+How much did you spend on Groceries?
(Enter numeric values only)
-Sri Athithya Kruth, [19.10.21 21:14]
-1212
-
-dollarbot, [19.10.21 21:14]
-The following expenditure has been recorded: You have spent $1212.0 for Food on 19-Oct-2021 21:14
+Rutuja Rashinkar, [19-10-2023 08:28 PM]
+45
\ No newline at end of file
diff --git a/docs/add_category.md b/docs/add_category.md
new file mode 100644
index 000000000..7cb304621
--- /dev/null
+++ b/docs/add_category.md
@@ -0,0 +1,45 @@
+# About DollarSplitBot's /add_category Feature
+This feature allows users to tell the chatbot what kind of expenses they want to track. If they choose a category that already exists, they are informed. If they choose a new category, it is added to their list of expense categories, and the chatbot starts tracking their spending for that category. This helps users keep a record of their expenses in different categories, like food, transportation, or shopping.
+
+Currently we have the following expense categories set by default:
+
+- Food
+- Groceries
+- Utilities
+- Transport
+- Shopping
+- Miscellaneous
+
+
+# Location of Code for this Feature
+The code that implements this feature can be found [here](https://github.com/shonilbhide/dollar_bot/blob/main/code/add_category.py)
+
+# Code Description
+## Functions
+
+1. run(message, bot):
+This function is the entry point for the "add" feature. When a user interacts with the bot, it pops up a menu asking the user to select an expense category. The function takes two arguments:
+ - `message`: This is a message from the user, containing their selection.
+ - `bot`: A reference to the Telegram bot object.
+
+2. post_append_spend(message, bot):
+This function is called after the user selects an expense category. It takes two arguments:
+ - `message`: The message from the user containing the selected category.
+ - `bot`: A reference to the Telegram bot object.
+
+The function then processes the user's choice. If the selected category already exists, it informs the user. If it doesn't exist, it adds the category to the list of expense categories and stores it in the user's expense tracker.
+
+# How to run this feature?
+Once the project is running(please follow the instructions given in the main README.md for this), please type /add_category into the telegram bot.
+
+Sho, [13-10-2023 15:04]
+/add_category
+
+testbot_SSAR, [13-10-2023 15:13]
+Please enter your category
+
+Sho, [13-10-2023 15:13]
+vehicle
+
+testbot_SSAR, [13-10-2023 15:13]
+The following category has been added: vehicle
\ No newline at end of file
diff --git a/docs/add_user.md b/docs/add_user.md
new file mode 100644
index 000000000..134d82c1d
--- /dev/null
+++ b/docs/add_user.md
@@ -0,0 +1,30 @@
+# About DollarSplitBot's /add_user Feature
+This feature is a part of a Telegram bot's functionality that allows users to register new people or names in a chat. The primary purpose of this code is to handle the registration of people's names within a specific chat and manage the list of registered users.
+
+# Location of Code for this Feature
+The code that implements this feature can be found [here](https://github.com/shonilbhide/dollar_bot/blob/main/code/add_user.py)
+
+# Code Description
+## Functions
+
+1. `register_people(message, bot, user_list)`:
+ - This function initiates the registration process when a user sends a message to the bot.
+ - It checks if the user's chat ID is not in the `user_list`, a data structure that stores user information.
+ - If the chat ID is not in `user_list`, it creates a new user record using `helper.createNewUserRecord(message)` and adds it to the `user_list`.
+ - It sets up a keyboard interface for user input and prompts the user to enter the name of the person they want to register.
+ - After the user provides a name, it registers the name and sets up the next step handler to handle further options.
+
+2. `add_person(message, bot, registered_users, user_list)`:
+ - This function is called after the user provides the name they want to register.
+ - It checks if the provided name is unique for the chat. If the name is already registered, it informs the user. Otherwise, it adds the name to the list of registered users.
+ - It provides feedback to the user about the successful registration of the name and offers further options using a keyboard interface.
+ - Depending on the user's choice, it can either prompt the user to register another person or finish the registration process.
+
+3. `handle_registration_choice(message, bot, registered_users, user_list)`:
+ - This function handles the user's choice after registering a person.
+ - It checks the user's choice and acts accordingly. If the user wants to register another person, it prompts for a name again. If the user chooses to finish registration, it stores the registered users and their related data in the `user_list`. This data includes who owes money to whom and how much.
+ - After storing the data, it displays the list of registered users or informs the user that no users are registered yet.
+
+
+# How to run this feature?
+This code is part of a Telegram bot's functionality that allows users to register people within a chat, stores information about registered users and their financial transactions, and provides a user-friendly interface for interacting with the bot during the registration process.
diff --git a/docs/code.md b/docs/code.md
index 096a6524c..01ef3c272 100644
--- a/docs/code.md
+++ b/docs/code.md
@@ -2,34 +2,21 @@
code.py is the main file from where calls to the corresponding .py files for all features are sent. It contains a number of endpoints which redirect to function calls in the corresponding files.
# Location of Code for this Feature
-The code that implements this feature can be found [here](https://github.com/sak007/MyDollarBot-BOTGo/blob/main/code/code.py)
+The code that implements this feature can be found [here](https://github.com/shonilbhide/dollar_bot/blob/Rubrics/code/code.py)
+
# Code Description
## Functions
-1. main()
-The entire bot's execution begins here. It ensure the **bot** variable begins polling and actively listening for requests from telegram.
-
-2. listener(user_requests):
-Takes 1 argument **user_requests** and logs all user interaction with the bot including all bot commands run and any other issue logs.
-
-3. start_and_menu_command(m):
-Prints out the the main menu displaying the features that the bot offers and the corresponding commands to be run from the Telegram UI to use these features. Commands used to run this: commands=['start', 'menu']
-
-4. command_add(message)
-Takes 1 argument **message** which contains the message from the user along with the chat ID of the user chat. It then calls add.py to run to execute the add functionality. Commands used to run this: commands=['add']
+- run(message, bot): This function serves as the entry point for the budget feature. It displays a menu in the chatbot, prompting the user to select an operation related to their budget. The available options are determined by the helper.getBudgetOptions() function. Once the user makes a selection, the control is passed to the post_operation_selection(message, bot) function for further processing.
-5. command_history(message):
-Takes 1 argument **message** which contains the message from the user along with the chat ID of the user chat. It then calls history.py to run to execute the add functionality. Commands used to run this: commands=['history']
+- post_operation_selection(message, bot): This function processes the user's selection of a budget operation. It checks if the selected operation is valid and, if not, informs the user that the operation is invalid. If the user is new and doesn't have a budget record, it initializes one. Depending on the selected operation (e.g., add, update, view, delete), it calls the respective sub-module functions to perform the desired operation and then stores the updated budget data using helper.write_json(user_list).
-6. command_edit(message):
-Takes 1 argument **message** which contains the message from the user along with the chat ID of the user chat. It then calls edit.py to run to execute the add functionality. Commands used to run this: commands=['edit']
+- budget_update.run(message, bot): This function is called when the user selects the "add" or "update" operation. It handles the process of adding or updating budget expenses. The exact details of these operations are likely implemented in the budget_update module.
-7. command_display(message):
-Takes 1 argument **message** which contains the message from the user along with the chat ID of the user chat. It then calls display.py to run to execute the add functionality. Commands used to run this: commands=['display']
+- budget_view.run(message, bot): This function is called when the user selects the "view" operation. It is responsible for displaying the user's budget information, such as expenses and balances. The specific implementation of the viewing process is likely found in the budget_view module.
-8. command_delete(message):
-Takes 1 argument **message** which contains the message from the user along with the chat ID of the user chat. It then calls delete.py to run to execute the add functionality. Commands used to run this: commands=['display']
+- budget_delete.run(message, bot): This function is called when the user selects the "delete" operation. It is responsible for managing the process of deleting specific budget expenses. The details of how the deletion process works are likely defined in the budget_delete module.
# How to run this feature?
-This file contains information on the main code.py file from where all features are run. Instructions to run this are the same as instructions to run the project and can be found in README.md.
\ No newline at end of file
+This file contains information on the main code.py file from where all features are run. Instructions to run this are the same as instructions to run the project and can be found in README.md.
diff --git a/docs/dollar_bot/**/*.html b/docs/dollar_bot/**/*.html
deleted file mode 100644
index e69de29bb..000000000
diff --git a/docs/dollar_bot/code/*.html b/docs/dollar_bot/code/*.html
deleted file mode 100644
index e69de29bb..000000000
diff --git a/docs/graphing.md b/docs/graphing.md
index 36ae40d9b..efbd38542 100644
--- a/docs/graphing.md
+++ b/docs/graphing.md
@@ -4,16 +4,16 @@ This feature enables the user to see their expense in a graphical format to enab
Currently, the /display command will provide the expenses as a message to the users via the bot. To better the UX, we decided to add the option to show the expenses in a Bar Graph.
# Location of Code for this Feature
-The code that implements this feature can be found [here](https://github.com/sak007/MyDollarBot-BOTGo/blob/main/code/graphing.py)
+The code that implements this feature can be found [here](https://github.com/shonilbhide/dollar_bot/blob/Rubrics/code/graphing.py)
# Code Description
## Functions
-1. visualize(total_text):
-This is the main function used to implement the graphing part of display feature. This file is called from display.py, and takes the user expense as a string and creates a dictionary which in turn is fed as input matplotlib to create the graph
+- viewBudget(data): This function creates a pie chart to visualize different budget categories. It takes a dictionary of budget data as input and generates a graph that represents the budget distribution across various categories. To provide a visual representation of how the budget is allocated in different spending categories.
-2. addlabels(x, y):
-This function is used to add the labels to the graph. It takes the expense values and adds the values inside the bar graph for each expense type
+- addlabels(x, y): This function is used to add labels to the bar graph. It takes two lists, 'x' (category names) and 'y' (expenditure values), and adds the corresponding values inside the bars of a bar graph. To make the bar graph more informative by labeling each bar with its expenditure value.
+
+- visualize(total_text, monthly_budget): This is the main function that generates a bar graph to compare actual expenditure with the budget for different categories. It takes two inputs: 'total_text,' which contains information about actual expenses, and 'monthly_budget,' which is a dictionary specifying the budget for each category. To create a visual comparison between the user's actual expenditures and their budgeted amounts for different spending categories.
# How to run this feature?
After you've added sufficient input data, use the /display command and you can see the output in a pictorial representation.
diff --git a/docs/helper.md b/docs/helper.md
index 54b993b7f..e7dca7d26 100644
--- a/docs/helper.md
+++ b/docs/helper.md
@@ -2,42 +2,32 @@
The helper file contains a set of functions that are commonly used for repeated tasks in the various features of MyDollarBot. Since these come up often, we have put them all up here in a separate file for reusability.
# Location of Code for this Feature
-The code that implements this feature can be found [here](https://github.com/sak007/MyDollarBot-BOTGo/blob/main/code/helper.py)
+The code that implements this feature can be found [here](https://github.com/shonilbhide/dollar_bot/blob/main/code/helper.py)
# Code Description
## Functions
-1. read_json():
-Function to load .json expense record data
+- spend_categories: This is a list of predefined spending categories, such as "Food," "Groceries," "Utilities," etc. These categories are used to categorize expenses.
-2. write_json(user_list):
-Stores data into the datastore of the bot.
+- choices: A list containing three options: "Date," "Category," and "Cost." These options might be used to select how the user wants to view or filter their expense data.
-3. validate_entered_amount(amount_entered):
-Takes 1 argument, **amount_entered**. It validates this amount's format to see if it has been correctly entered by the user.
+- spend_display_option: This list contains two options: "Day" and "Month." These options could be used to specify whether the user wants to view expenses on a daily or monthly basis.
-4. getUserHistory(chat_id):
-Takes 1 argument **chat_id** and uses this to get the relevant user's historical data.
+- spend_estimate_option: Another list with two options: "Next day" and "Next month." These might be used to estimate future expenses based on past spending data.
-5. getSpendCategories():
-This functions returns the spend categories used in the bot. These are defined the same file.
+- update_options: A dictionary with two key-value pairs, "continue" and "exit." These options could be used to continue or exit a particular process within the program.
-6. getSpendDisplayOptions():
-This functions returns the spend display options used in the bot. These are defined the same file.
+- budget_options: A dictionary with options related to budget management, such as "add," "update," "view," and "delete."
-7. getCommands():
-This functions returns the command options used in the bot. These are defined the same file.
+- budget_types: A dictionary that defines different types of budgets, such as "Overall Budget" and "Category-Wise Budget."
-8. def getDateFormat():
-This functions returns the date format used in the bot.
+- data_format: A dictionary that seems to be an initial data structure for storing user data, expenses, and budgets. It has placeholders for various data, including user information, expenses, and budget details.
-9. def getTimeFormat():
-This functions returns the time format used in the bot.
+- commands: A dictionary that provides descriptions of various commands or actions that the user can perform within the program. These descriptions include commands like "add," "display," "edit," etc.
-10. def getMonthFormat():
-This functions returns the month format used in the bot.
+Functions: The code defines several functions, including read_json, write_json, validate_entered_amount, and others. These functions likely handle reading and writing data, validating user input, and managing user records and budgets
# How to run this feature?
Once the project is running(please follow the instructions given in the main README.md for this), please type /add into the telegram bot.
-This file is not a feature and cannot be run per se. Its functions are used all over by the other files as it provides helper functions for various functionalities and features.
\ No newline at end of file
+This file is not a feature and cannot be run per se. Its functions are used all over by the other files as it provides helper functions for various functionalities and features.
diff --git a/docs/history.md b/docs/history.md
index c259a341a..88c0e2721 100644
--- a/docs/history.md
+++ b/docs/history.md
@@ -2,34 +2,22 @@
This feature enables the user to view all of their stored records i.e it gives a historical view of all the expenses stored in MyDollarBot.
# Location of Code for this Feature
-The code that implements this feature can be found [here](https://github.com/sak007/MyDollarBot-BOTGo/blob/main/code/history.py)
+The code that implements this feature can be found [here](https://github.com/shonilbhide/dollar_bot/blob/main/code/history.py)
# Code Description
## Functions
-1. run(message, bot):
-This is the main function used to implement the delete feature. It takes 2 arguments for processing - **message** which is the message from the user, and **bot** which is the telegram bot object from the main code.py function. It calls helper.py to get the user's historical data and based on whether there is data available, it either prints an error message or displays the user's historical data.
+- run(message, bot): This is the primary function that handles the display of historical spending data for a user.
+How it works: It takes two inputs, message (a message from the user) and bot (a communication tool for the bot). The function first tries to read the user's historical data, and if it finds any data, it formats it into a tabular view. Then it sends this data back to the user. If no data is found, it informs the user that there are no records. To provide the user with their spending history.
-# How to run this feature?
-Once the project is running(please follow the instructions given in the main README.md for this), please type /add into the telegram bot.
-
-Below you can see an example in text format:
+- helper.read_json(): This function reads data from a JSON file. To retrieve the user's historical data.
-Sri Athithya Kruth, [20.10.21 20:33]
-/display
+- helper.getUserHistory(chat_id): This function retrieves a specific user's spending history. To obtain the user's historical spending records based on their chat ID.
-Sri Athithya Kruth, [20.10.21 20:33]
-Day
+- bot.send_message(chat_id, tabular_data, parse_mode="Markdown"): This sends the formatted spending data back to the user in a message, allowing the user to view their historical data in a neat table format. To present the historical data to the user in a visually appealing way.
-mydollarbot20102021, [20.10.21 20:33]
-Hold on! Calculating...
+- Exception Handling: The code is prepared to handle exceptions. If any errors occur during the process, it logs the error and informs the user about the issue. To ensure that the user receives feedback in case something goes wrong, and to keep a record of errors for debugging.
-Sri Athithya Kruth, [20.10.21 20:53]
-/history
+# How to run this feature?
+Once the project is running(please follow the instructions given in the main README.md for this), please type /add into the telegram bot.
-mydollarbot20102021, [20.10.21 20:53]
-Here is your spending history :
-DATE, CATEGORY, AMOUNT
-----------------------
-20-Oct-2021 20:33,Transport,1022.0
-20-Oct-2021 20:33,Groceries,12.0
\ No newline at end of file
diff --git a/docs/notifier.md b/docs/notifier.md
new file mode 100644
index 000000000..f7bf35f96
--- /dev/null
+++ b/docs/notifier.md
@@ -0,0 +1,14 @@
+# About DollarSplitBot's notifier.py file
+notifier.py is used to send notifications to a Telegram chat. This can be helpful for various purposes, like receiving updates or alerts from a program or service.
+# Location of Code for this Feature
+The code that implements this feature can be found [here](https://github.com/shonilbhide/dollar_bot/blob/main/code/notifier.py)
+
+# Code Description
+## Functions
+- _get_chat_id method: This is a private method within the TelegramNotifier class. It attempts to fetch the chat ID by making an API request to Telegram. The chat ID is essential to send messages to a specific chat or group. If it fails to fetch the chat ID, it sets the chat ID to None and prints an error message.
+
+- send method: This method is used to send a message to the chat or group associated with the TelegramNotifier object. It takes a msg (message) as an argument, which is the text to be sent.
+Before sending the message, it checks if the chat ID is available. If not, it attempts to retrieve it. It constructs a payload containing the chat ID and the message and sends it to the Telegram API. If the message is sent successfully, it prints a success message. If there's an error, it prints an error message.
+
+# How to run this feature?
+This file contains information on the main code.py file from where all features are run. Instructions to run this are the same as instructions to run the project and can be found in README.md.
diff --git a/docs/notify.md b/docs/notify.md
new file mode 100644
index 000000000..12b61986f
--- /dev/null
+++ b/docs/notify.md
@@ -0,0 +1,19 @@
+# About DollarSplitBot's code.py file
+notify.py is is for a program that helps users manage their budgets and sends notifications when they exceed their budget for a specific category.
+
+# Location of Code for this Feature
+The code that implements this feature can be found [here](https://github.com/shonilbhide/dollar_bot/blob/main/code/notify.py)
+
+# Code Description
+## Functions
+- notify(chat_id, cat, amount): This function sends a notification on Telegram when the user exceeds their budget for a specific category. It takes three arguments:
+chat_id: The user's Telegram chat ID, which helps the program send the notification to the correct user.
+cat: The category for which the budget is exceeded (e.g., "Groceries" or "Transportation").
+amount: The amount by which the budget is exceeded.
+This function reads some configuration data from a file (specifically, an "api_token"), which is required to send the Telegram notification. Then, it formats a message to inform the user that they've exceeded their budget for a particular category and sends it to the user on Telegram.
+
+# How to run this feature?
+Once the project is running(please follow the instructions given in the main README.md for this), please type /budget into the telegram bot.
+
+
+
diff --git a/docs/pdf.md b/docs/pdf.md
new file mode 100644
index 000000000..a0bbe41f9
--- /dev/null
+++ b/docs/pdf.md
@@ -0,0 +1,17 @@
+# About DollarSplitBot's /pdf Feature
+The provided code appears to be part of a Python script designed to create PDF documents based on user input in a chat application, possibly a Telegram bot.
+
+The user can choose a category and add the amount for the budget to be stored in the expense tracker.
+
+# Location of Code for this Feature
+The code that implements this feature can be found [here](https://github.com/shonilbhide/dollar_bot/blob/main/code/pdf.py)
+
+# Code Description
+## Functions
+
+- run(message, bot): This is the main function for implementing the PDF save feature. It reads some data, displays a menu asking the user what kind of PDF they want to generate, and registers a handler for the user's response.
+
+- pdfGeneration(message, bot, user_list, user_history): This function generates PDF documents based on user preferences. Depending on the user's choice, it can generate two types of PDF documents: one showing total expenses categorized, and the other showing who owes whom how much. It uses the matplotlib, tabulate, and fpdf libraries to create these PDFs.
+
+# How to run this feature?
+Once the project is running(please follow the instructions given in the main README.md for this), please type /pdf into the telegram bot.
diff --git a/project_key b/project_key
new file mode 100644
index 000000000..d77b91012
--- /dev/null
+++ b/project_key
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACDz6s1RaZh+YlECZSe4IkDdeypsj1AqdrwHGVle1jryxQAAAJhQIARzUCAE
+cwAAAAtzc2gtZWQyNTUxOQAAACDz6s1RaZh+YlECZSe4IkDdeypsj1AqdrwHGVle1jryxQ
+AAAEBlj6niPKC2QbaHsmwjhYw7uRe75GHf3vs9sUvldZTdlvPqzVFpmH5iUQJlJ7giQN17
+KmyPUCp2vAcZWV7WOvLFAAAAFWJoaWRlc2hvbmlsQGdtYWlsLmNvbQ==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/project_key.pub b/project_key.pub
new file mode 100644
index 000000000..543baf512
--- /dev/null
+++ b/project_key.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPPqzVFpmH5iUQJlJ7giQN17KmyPUCp2vAcZWV7WOvLF bhideshonil@gmail.com
diff --git a/python_docs/code.html b/python_docs/code.html
index 187903a33..b02ce5b58 100644
--- a/python_docs/code.html
+++ b/python_docs/code.html
@@ -158,7 +158,10 @@ Documen
faq_message = '"What does this bot do?"\n' + \
'>> DollarBot lets you manage your expenses so you can always stay on top of them! \n\n' + \
- '"How can I add an epxense?" \n' + \
+ '"How can I add a category?" \n' + \
+ '>> Type /add_category, then add a category for the expense. \n\n' + \
+
+ '"How can I add an expense?" \n' + \
'>> Type /add, then select a category to type the expense. \n\n' + \
'"Can I see history of my expenses?" \n' + \
'>> Yes! Use /display to get a graphical display, or /history to view detailed summary.\n\n' + \
diff --git a/requirements.txt b/requirements.txt
index 082b6f120..ba0924101 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,4 +5,6 @@ flake8
matplotlib
coverage
pytest-mock
-python-telegram-bot-calendar
\ No newline at end of file
+python-telegram-bot-calendar
+tabulate
+FPDF
\ No newline at end of file
diff --git a/test/test_add.py b/test/test_add.py
index 0f42fa26e..6dd427764 100644
--- a/test/test_add.py
+++ b/test/test_add.py
@@ -1,59 +1,60 @@
-import os
-import json
-from mock.mock import patch
+import pytest
+from unittest.mock import patch
from telebot import types
+from datetime import datetime
from code import add
-from mock import ANY
dateFormat = "%d-%b-%Y"
timeFormat = "%H:%M"
monthFormat = "%b-%Y"
-
@patch("telebot.telebot")
-def test_run(mock_telebot, mocker):
+@patch("add.helper.read_json")
+def test_run(user_mock,mock_telebot):
mc = mock_telebot.return_value
+ user_mock.return_value = create_user_list()
mc.reply_to.return_value = True
message = create_message("hello from test run!")
add.run(message, mc)
assert not mc.reply_to.called
-
@patch("telebot.telebot")
def test_post_category_selection_working(mock_telebot, mocker):
mc = mock_telebot.return_value
mc.send_message.return_value = True
-
message = create_message("hello from testing!")
- add.post_category_selection(message, mc)
+ user_list = create_user_list()
+ paid_by = 'User1'
+ owed_by = ['User1']
+ add.post_category_selection(message, mc,owed_by,paid_by,user_list)
assert mc.send_message.called
-
@patch("telebot.telebot")
def test_post_category_selection_noMatchingCategory(mock_telebot, mocker):
mc = mock_telebot.return_value
mc.send_message.return_value = []
mc.reply_to.return_value = True
-
mocker.patch.object(add, "helper")
+ user_list = create_user_list()
+ paid_by = 'User1'
+ owed_by = ['User1','User2']
add.helper.getSpendCategories.return_value = None
-
- message = create_message("hello from testing!")
- add.post_category_selection(message, mc)
+ message = create_message("Food")
+ add.post_category_selection(message, mc,owed_by,paid_by,user_list)
assert mc.reply_to.called
-
@patch("telebot.telebot")
def test_post_amount_input_working(mock_telebot, mocker):
mc = mock_telebot.return_value
mc.send_message.return_value = True
-
- message = create_message("hello from testing!")
- add.post_category_selection(message, mc)
+ user_list = create_user_list()
+ paid_by = 'User1'
+ owed_by = ['User1','User2']
+ message = create_message("40")
+ add.post_category_selection(message, mc,owed_by,paid_by,user_list)
assert mc.send_message.called
-
@patch("telebot.telebot")
def test_post_amount_input_working_withdata(mock_telebot, mocker):
mc = mock_telebot.return_value
@@ -63,15 +64,16 @@ def test_post_amount_input_working_withdata(mock_telebot, mocker):
add.helper.write_json.return_value = True
add.helper.getDateFormat.return_value = dateFormat
add.helper.getTimeFormat.return_value = timeFormat
-
mocker.patch.object(add, "option")
add.option.return_value = {11, "here"}
-
+ user_list = create_user_list()
+ paid_by = 'User1'
+ owed_by = ['User1','User2']
+ message = create_message("40")
message = create_message("hello from testing!")
- add.post_amount_input(message, mc, "Food")
+ add.post_amount_input(message, mc, "Food",owed_by,paid_by,user_list)
assert mc.send_message.called
-
@patch("telebot.telebot")
def test_post_amount_input_nonworking(mock_telebot, mocker):
mc = mock_telebot.return_value
@@ -80,10 +82,9 @@ def test_post_amount_input_nonworking(mock_telebot, mocker):
mocker.patch.object(add, "helper")
add.helper.validate_entered_amount.return_value = 0
message = create_message("hello from testing!")
- add.post_amount_input(message, mc, "Food")
+ add.post_amount_input(message, mc, "Food",['User1','User2'],'User1',create_user_list())
assert mc.reply_to.called
-
@patch("telebot.telebot")
def test_post_amount_input_working_withdata_chatid(mock_telebot, mocker):
mc = mock_telebot.return_value
@@ -93,51 +94,48 @@ def test_post_amount_input_working_withdata_chatid(mock_telebot, mocker):
add.helper.write_json.return_value = True
add.helper.getDateFormat.return_value = dateFormat
add.helper.getTimeFormat.return_value = timeFormat
-
mocker.patch.object(add, "option")
add.option = {11, "here"}
test_option = {}
test_option[11] = "here"
add.option = test_option
-
message = create_message("hello from testing!")
- add.post_amount_input(message, mc, "Food")
+ add.post_amount_input(message, mc, "Food",['User1','User2'],'User1',create_user_list())
assert mc.send_message.called
- assert mc.send_message.called_with(11, ANY)
-
+ assert mc.send_message.called_with(11)
def test_add_user_record_nonworking(mocker):
mocker.patch.object(add, "helper")
add.helper.read_json.return_value = {}
- addeduserrecord = add.add_user_record(1, "record : test")
+ addeduserrecord = add.add_user_record(create_user_list(), "record : test",'11',
+ "{},{},{}".format('17-Oct-2023 13:23', 'Food', '40'),40,['User1','User2'],'User1')
assert addeduserrecord
-
def test_add_user_record_working(mocker):
- MOCK_USER_DATA = test_read_json()
+ MOCK_USER_DATA = create_user_list()
mocker.patch.object(add, "helper")
add.helper.read_json.return_value = MOCK_USER_DATA
- addeduserrecord = add.add_user_record(1, "record : test")
+ addeduserrecord = add.add_user_record(create_user_list(), "record : test",'11',
+ "{},{},{}".format('17-Oct-2023 13:23', 'Food', '40'),40,['User1','User2'],'User1')
if len(MOCK_USER_DATA) + 1 == len(addeduserrecord):
assert True
-
def create_message(text):
params = {"messagebody": text}
chat = types.User(11, False, "test")
- return types.Message(1, None, None, chat, "text", params, "")
-
-def test_read_json():
- try:
- if not os.path.exists("./test/dummy_expense_record.json"):
- with open("./test/dummy_expense_record.json", "w") as json_file:
- json_file.write("{}")
- return json.dumps("{}")
- elif os.stat("./test/dummy_expense_record.json").st_size != 0:
- with open("./test/dummy_expense_record.json") as expense_record:
- expense_record_data = json.load(expense_record)
- return expense_record_data
+ message = types.Message(1, None, None, chat, "text", params, "")
+ message.text = text
+ return message
+
+def create_user_list():
+ return {'users': ['User1'],
+ 'owed': {'User1': 0},
+ 'owing': {'User1': {}},
+ 'data': [],
+ 'csv_data': [],
+ 'budget': {'overall': '0', 'category': {'Food': '0', 'Groceries': '0', 'Utilities': '0', 'Transport': '0', 'Shopping': '0', 'Miscellaneous': '0'}},
+ '11': {'users': ['User1', 'User1', 'User2', 'User3', 'User4'], 'owed': {'User1': 57.5, 'User2': 0, 'User3': 0, 'User4': 0},
+ 'owing': {'User1': {}, 'User2': {'User1': 22.5}, 'User3': {'User1': 12.5}, 'User4': {'User1': 22.5}},
+ 'data': ['17-Oct-2023 13:16,Utilities,20.0', '17-Oct-2023 13:23,Transport,50.0', '18-Oct-2023 23:54,Food,30.0'], 'csv_data': ['17-Oct-2023 13:16,Utilities,20.0,Sho,User1', '17-Oct-2023 13:23,Transport,50.0,Sho,Sakshi & Rutuja & Sho & User4', '18-Oct-2023 23:54,Food,30.0,Sho,Sakshi & Sho & User4'], 'budget': {'overall': '0', 'category': {'Food': '0', 'Groceries': '0', 'Utilities': '0', 'Transport': '0', 'Shopping': '0', 'Miscellaneous': '0'}}}}
- except FileNotFoundError:
- print("---------NO RECORDS FOUND---------")
diff --git a/user.properties b/user.properties
index 6923b0754..fe5a204c9 100644
--- a/user.properties
+++ b/user.properties
@@ -1,2 +1,3 @@
api_token=2124576840:AAF4GNT5QuNmfnOFjfCwU4JPu2xOqkOVgJA
+