├── .github
└── workflows
│ ├── ci.yml
│ └── release.yml
├── .gitignore
├── LICENSE
├── README.md
├── TaskCore.App
├── IPriorityColorChooser.cs
├── Options
│ ├── AddCategoryOptions.cs
│ ├── AddTaskOptions.cs
│ ├── ClearDatabaseOptions.cs
│ ├── CompleteTaskOptions.cs
│ ├── DefaultOptions.cs
│ ├── ListTasksOptions.cs
│ ├── RemoveCategoryOptions.cs
│ └── RemoveTaskOptions.cs
├── PriorityColorChooser.cs
├── Program.cs
├── Properties
│ └── launchSettings.json
├── TaskCore.App.csproj
├── Verbs
│ ├── AddCategory.cs
│ ├── AddTask.cs
│ ├── ClearDatabase.cs
│ ├── CompleteTask.cs
│ ├── DefaultVerb.cs
│ ├── ListTasks.cs
│ ├── RemoveCategory.cs
│ └── RemoveTask.cs
└── Views
│ ├── AddCategoryErrorView.cs
│ ├── AddCategoryView.cs
│ ├── AddTaskNoCategoryView.cs
│ ├── AddTaskRequiredFieldsMissingView.cs
│ ├── AddTaskView.cs
│ ├── ClearDatabaseErrorView.cs
│ ├── ClearDatabaseView.cs
│ ├── CompleteTaskView.cs
│ ├── DefaultVerbView.cs
│ ├── ListTasksView.cs
│ ├── RemoveCategoryView.cs
│ └── RemoveTaskView.cs
├── TaskCore.Dal.FileSystem
├── CategoryRepository.cs
├── DbRepository.cs
├── FileManager.cs
├── TaskCore.Dal.FileSystem.csproj
├── TaskFileName.cs
└── TodoTaskRepository.cs
├── TaskCore.Dal.Interfaces
├── ICategoryRepository.cs
├── IDbRepository.cs
├── ITodoTaskRepository.cs
└── TaskCore.Dal.Interfaces.csproj
├── TaskCore.Dal.Models
├── Category.cs
├── TaskCore.Dal.Models.csproj
└── TodoTask.cs
├── TaskCore.sln
├── assests
└── banner.png
└── taskcore_demo.gif
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Continuous Integration
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: Setup .NET Core
17 | uses: actions/setup-dotnet@v1
18 | with:
19 | dotnet-version: 3.1.301
20 | - name: Install dependencies
21 | run: dotnet restore
22 | - name: Build
23 | run: dotnet build --configuration Release --no-restore
24 | - name: Test
25 | run: dotnet test --no-restore --verbosity normal
26 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Publish
2 |
3 | on:
4 | push:
5 | # Sequence of patterns matched against refs/tags
6 | tags:
7 | - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
8 |
9 | jobs:
10 | release:
11 | name: Release
12 | strategy:
13 | matrix:
14 | kind: ['linux', 'windows', 'macOS']
15 | include:
16 | - kind: linux
17 | os: ubuntu-latest
18 | target: linux-x64
19 | - kind: windows
20 | os: windows-latest
21 | target: win-x64
22 | - kind: macOS
23 | os: macos-latest
24 | target: osx-x64
25 | runs-on: ${{ matrix.os }}
26 | steps:
27 | - name: Checkout
28 | uses: actions/checkout@v1
29 |
30 | - name: Setup dotnet
31 | uses: actions/setup-dotnet@v1
32 | with:
33 | dotnet-version: 3.1.101
34 |
35 | - name: Build
36 | shell: bash
37 | run: |
38 | tag=$(git describe --tags --abbrev=0)
39 | release_name="TaskCore-$tag-${{ matrix.target }}"
40 |
41 | # Build everything
42 | dotnet publish "./TaskCore.App/TaskCore.App.csproj" --self-contained true -p:PublishSingleFile=true -p:PublishTrimmed=true --framework netcoreapp3.1 --runtime "${{ matrix.target }}" -c Release -o $release_name
43 |
44 | # Pack files
45 | if [ "${{ matrix.target }}" == "win-x64" ]; then
46 | # Pack to zip for Windows
47 | 7z a -tzip "${release_name}.zip" "./${release_name}/*"
48 | else
49 | tar czvf "${release_name}.tar.gz" "$release_name"
50 | fi
51 |
52 | # Delete output directory
53 | rm -r "$release_name"
54 |
55 | - name: Publish
56 | uses: softprops/action-gh-release@v1
57 | with:
58 | files: "TaskCore*"
59 | env:
60 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | .idea/**
14 |
15 | # User-specific files (MonoDevelop/Xamarin Studio)
16 | *.userprefs
17 |
18 | # Mono auto generated files
19 | mono_crash.*
20 |
21 | # Build results
22 | [Dd]ebug/
23 | [Dd]ebugPublic/
24 | [Rr]elease/
25 | [Rr]eleases/
26 | x64/
27 | x86/
28 | [Ww][Ii][Nn]32/
29 | [Aa][Rr][Mm]/
30 | [Aa][Rr][Mm]64/
31 | bld/
32 | [Bb]in/
33 | [Oo]bj/
34 | [Ll]og/
35 | [Ll]ogs/
36 |
37 | # Visual Studio 2015/2017 cache/options directory
38 | .vs/
39 | # Uncomment if you have tasks that create the project's static files in wwwroot
40 | #wwwroot/
41 |
42 | # Visual Studio 2017 auto generated files
43 | Generated\ Files/
44 |
45 | # MSTest test Results
46 | [Tt]est[Rr]esult*/
47 | [Bb]uild[Ll]og.*
48 |
49 | # NUnit
50 | *.VisualState.xml
51 | TestResult.xml
52 | nunit-*.xml
53 |
54 | # Build Results of an ATL Project
55 | [Dd]ebugPS/
56 | [Rr]eleasePS/
57 | dlldata.c
58 |
59 | # Benchmark Results
60 | BenchmarkDotNet.Artifacts/
61 |
62 | # .NET Core
63 | project.lock.json
64 | project.fragment.lock.json
65 | artifacts/
66 |
67 | # ASP.NET Scaffolding
68 | ScaffoldingReadMe.txt
69 |
70 | # StyleCop
71 | StyleCopReport.xml
72 |
73 | # Files built by Visual Studio
74 | *_i.c
75 | *_p.c
76 | *_h.h
77 | *.ilk
78 | *.meta
79 | *.obj
80 | *.iobj
81 | *.pch
82 | *.pdb
83 | *.ipdb
84 | *.pgc
85 | *.pgd
86 | *.rsp
87 | *.sbr
88 | *.tlb
89 | *.tli
90 | *.tlh
91 | *.tmp
92 | *.tmp_proj
93 | *_wpftmp.csproj
94 | *.log
95 | *.vspscc
96 | *.vssscc
97 | .builds
98 | *.pidb
99 | *.svclog
100 | *.scc
101 |
102 | # Chutzpah Test files
103 | _Chutzpah*
104 |
105 | # Visual C++ cache files
106 | ipch/
107 | *.aps
108 | *.ncb
109 | *.opendb
110 | *.opensdf
111 | *.sdf
112 | *.cachefile
113 | *.VC.db
114 | *.VC.VC.opendb
115 |
116 | # Visual Studio profiler
117 | *.psess
118 | *.vsp
119 | *.vspx
120 | *.sap
121 |
122 | # Visual Studio Trace Files
123 | *.e2e
124 |
125 | # TFS 2012 Local Workspace
126 | $tf/
127 |
128 | # Guidance Automation Toolkit
129 | *.gpState
130 |
131 | # ReSharper is a .NET coding add-in
132 | _ReSharper*/
133 | *.[Rr]e[Ss]harper
134 | *.DotSettings.user
135 |
136 | # TeamCity is a build add-in
137 | _TeamCity*
138 |
139 | # DotCover is a Code Coverage Tool
140 | *.dotCover
141 |
142 | # AxoCover is a Code Coverage Tool
143 | .axoCover/*
144 | !.axoCover/settings.json
145 |
146 | # Coverlet is a free, cross platform Code Coverage Tool
147 | coverage*[.json, .xml, .info]
148 |
149 | # Visual Studio code coverage results
150 | *.coverage
151 | *.coveragexml
152 |
153 | # NCrunch
154 | _NCrunch_*
155 | .*crunch*.local.xml
156 | nCrunchTemp_*
157 |
158 | # MightyMoose
159 | *.mm.*
160 | AutoTest.Net/
161 |
162 | # Web workbench (sass)
163 | .sass-cache/
164 |
165 | # Installshield output folder
166 | [Ee]xpress/
167 |
168 | # DocProject is a documentation generator add-in
169 | DocProject/buildhelp/
170 | DocProject/Help/*.HxT
171 | DocProject/Help/*.HxC
172 | DocProject/Help/*.hhc
173 | DocProject/Help/*.hhk
174 | DocProject/Help/*.hhp
175 | DocProject/Help/Html2
176 | DocProject/Help/html
177 |
178 | # Click-Once directory
179 | publish/
180 |
181 | # Publish Web Output
182 | *.[Pp]ublish.xml
183 | *.azurePubxml
184 | # Note: Comment the next line if you want to checkin your web deploy settings,
185 | # but database connection strings (with potential passwords) will be unencrypted
186 | *.pubxml
187 | *.publishproj
188 |
189 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
190 | # checkin your Azure Web App publish settings, but sensitive information contained
191 | # in these scripts will be unencrypted
192 | PublishScripts/
193 |
194 | # NuGet Packages
195 | *.nupkg
196 | # NuGet Symbol Packages
197 | *.snupkg
198 | # The packages folder can be ignored because of Package Restore
199 | **/[Pp]ackages/*
200 | # except build/, which is used as an MSBuild target.
201 | !**/[Pp]ackages/build/
202 | # Uncomment if necessary however generally it will be regenerated when needed
203 | #!**/[Pp]ackages/repositories.config
204 | # NuGet v3's project.json files produces more ignorable files
205 | *.nuget.props
206 | *.nuget.targets
207 |
208 | # Microsoft Azure Build Output
209 | csx/
210 | *.build.csdef
211 |
212 | # Microsoft Azure Emulator
213 | ecf/
214 | rcf/
215 |
216 | # Windows Store app package directories and files
217 | AppPackages/
218 | BundleArtifacts/
219 | Package.StoreAssociation.xml
220 | _pkginfo.txt
221 | *.appx
222 | *.appxbundle
223 | *.appxupload
224 |
225 | # Visual Studio cache files
226 | # files ending in .cache can be ignored
227 | *.[Cc]ache
228 | # but keep track of directories ending in .cache
229 | !?*.[Cc]ache/
230 |
231 | # Others
232 | ClientBin/
233 | ~$*
234 | *~
235 | *.dbmdl
236 | *.dbproj.schemaview
237 | *.jfm
238 | *.pfx
239 | *.publishsettings
240 | orleans.codegen.cs
241 |
242 | # Including strong name files can present a security risk
243 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
244 | #*.snk
245 |
246 | # Since there are multiple workflows, uncomment next line to ignore bower_components
247 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
248 | #bower_components/
249 |
250 | # RIA/Silverlight projects
251 | Generated_Code/
252 |
253 | # Backup & report files from converting an old project file
254 | # to a newer Visual Studio version. Backup files are not needed,
255 | # because we have git ;-)
256 | _UpgradeReport_Files/
257 | Backup*/
258 | UpgradeLog*.XML
259 | UpgradeLog*.htm
260 | ServiceFabricBackup/
261 | *.rptproj.bak
262 |
263 | # SQL Server files
264 | *.mdf
265 | *.ldf
266 | *.ndf
267 |
268 | # Business Intelligence projects
269 | *.rdl.data
270 | *.bim.layout
271 | *.bim_*.settings
272 | *.rptproj.rsuser
273 | *- [Bb]ackup.rdl
274 | *- [Bb]ackup ([0-9]).rdl
275 | *- [Bb]ackup ([0-9][0-9]).rdl
276 |
277 | # Microsoft Fakes
278 | FakesAssemblies/
279 |
280 | # GhostDoc plugin setting file
281 | *.GhostDoc.xml
282 |
283 | # Node.js Tools for Visual Studio
284 | .ntvs_analysis.dat
285 | node_modules/
286 |
287 | # Visual Studio 6 build log
288 | *.plg
289 |
290 | # Visual Studio 6 workspace options file
291 | *.opt
292 |
293 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
294 | *.vbw
295 |
296 | # Visual Studio LightSwitch build output
297 | **/*.HTMLClient/GeneratedArtifacts
298 | **/*.DesktopClient/GeneratedArtifacts
299 | **/*.DesktopClient/ModelManifest.xml
300 | **/*.Server/GeneratedArtifacts
301 | **/*.Server/ModelManifest.xml
302 | _Pvt_Extensions
303 |
304 | # Paket dependency manager
305 | .paket/paket.exe
306 | paket-files/
307 |
308 | # FAKE - F# Make
309 | .fake/
310 |
311 | # CodeRush personal settings
312 | .cr/personal
313 |
314 | # Python Tools for Visual Studio (PTVS)
315 | __pycache__/
316 | *.pyc
317 |
318 | # Cake - Uncomment if you are using it
319 | # tools/**
320 | # !tools/packages.config
321 |
322 | # Tabs Studio
323 | *.tss
324 |
325 | # Telerik's JustMock configuration file
326 | *.jmconfig
327 |
328 | # BizTalk build output
329 | *.btp.cs
330 | *.btm.cs
331 | *.odx.cs
332 | *.xsd.cs
333 |
334 | # OpenCover UI analysis results
335 | OpenCover/
336 |
337 | # Azure Stream Analytics local run output
338 | ASALocalRun/
339 |
340 | # MSBuild Binary and Structured Log
341 | *.binlog
342 |
343 | # NVidia Nsight GPU debugger configuration file
344 | *.nvuser
345 |
346 | # MFractors (Xamarin productivity tool) working folder
347 | .mfractor/
348 |
349 | # Local History for Visual Studio
350 | .localhistory/
351 |
352 | # BeatPulse healthcheck temp database
353 | healthchecksdb
354 |
355 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
356 | MigrationBackup/
357 |
358 | # Ionide (cross platform F# VS Code tools) working folder
359 | .ionide/
360 |
361 | # Fody - auto-generated XML schema
362 | FodyWeavers.xsd
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2020 Tarik Guney
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | Table of Contents
4 | =================
5 |
6 | * [What is TaskCore?](#what-is-taskcore)
7 | * [Why did you name it TaskCore?](#why-did-you-name-it-taskcore)
8 | * [Why?](#why)
9 | * [Demo](#demo)
10 | * [How to contribute?](#how-to-contribute)
11 | * [How it works?](#how-it-works)
12 | * [Managing Tasks](#managing-tasks)
13 | * [Managing Categories](#managing-categories)
14 | * [Clear Database](#clear-database)
15 | * [Configuration](#configuration)
16 | * [CommandCore Framework](#commandcore-framework)
17 | * [Installation](#installation)
18 | * [Want to build locally?](#want-to-build-locally)
19 | * [Linux](#linux)
20 | * [Windows 10](#windows-10)
21 | * [MacOS](#macos)
22 |
23 |
24 |
25 |
26 |
27 | # What is TaskCore?
28 |
29 | It is a simple **cross-platform ToDo management application** for those who live inside terminals.
30 |
31 | This application works almost on any platform which includes Linux Distros, MacOS, and Windows. It does not require any runtime to be installed on the target machine, and it is only one single executable that can be copied anywhere.
32 |
33 | ## Why did you name it TaskCore?
34 |
35 | "Task" simply represents what this application is helping people with. "Core" represents its simplicity and how it works on a terminal environment.
36 |
37 | # Why?
38 |
39 | Why not? Not everyone likes fancy GUIs or works in X Window environments or prefer leaving their simple looking terminal environment. This application is not for everyone. You should have a special taste for the command line environments.
40 |
41 | # Demo
42 |
43 | 
44 |
45 | ## How to contribute?
46 |
47 | I am adding the new features I am planning to add here: https://github.com/tarikguney/taskcore/projects/1. You can grab one of the not active one, and let me know if you are working on them. You can also be a beta tester and create issues here if you see any problem: https://github.com/tarikguney/taskcore/issues. Thanks for considering to contribute to the project.
48 |
49 | # How it works?
50 |
51 | ## Managing Tasks
52 |
53 | Add a new task item with priority 1 (highest) and due date:
54 | ```bash
55 | taskcore add -n "Finish up Task Core" -d 10/11/2020 -p 1
56 | ```
57 |
58 | Complete a task item - Passing multiple Ids is allowed:
59 | ```bash
60 | taskcore c -i 3 2 4
61 | ```
62 |
63 | List all active task items:
64 | ```bash
65 | taskcore ls
66 | ```
67 |
68 | List all task items including completed ones:
69 |
70 | ```bash
71 | taskcore ls -c
72 | ```
73 |
74 | List tasks under a given category:
75 | ```
76 | taskcore ls -ca Project
77 | ```
78 |
79 | Remove a task item - Passing multiple Ids is allowed:
80 | ```bash
81 | taskcore rm -i 3 2 3
82 | ```
83 |
84 | ## Managing Categories
85 |
86 | Add a new category:
87 |
88 | ```bash
89 | taskcore addc -n "Work"
90 | ```
91 | Add a task item to a category:
92 |
93 | ```c#
94 | taskcore add -t "Check if the command works" -d 01/01/2010 -c Work
95 | ```
96 | Remove a category:
97 |
98 | ```bash
99 | taskcore rmc -n "Work"
100 | ```
101 |
102 | ## Clear Database
103 |
104 | Delete the database folder, `--force` is required:
105 |
106 | ```bash
107 | taskcore clear --force
108 | ```
109 |
110 | ## Configuration
111 |
112 | TaskCore supports the following environment variables:
113 |
114 |
115 |
116 | Variable |
117 | Description |
118 |
119 |
120 | TASKCORE_DB_PATH |
121 | Set the DB path |
122 |
123 |
124 |
125 | # CommandCore Framework
126 |
127 | This application is using another project I developed named "CommandCore" as its underlying framework for command parsing and project layout management. CommandCore is an opinionated MVC-based framework for command line application development. You can find more information at [CommandCore Github Page](https://www.github.com/tarikguney/command-core).
128 |
129 | # Installation
130 |
131 | You can download an executable for your system: [here](https://github.com/tarikguney/taskcore/releases)
132 |
133 | Alternatively, taskcore is available in the following package managers:
134 |
135 |
136 |
137 | Arch Linux AUR
138 | (build from source) |
139 | yay -S taskcore
140 | or
141 | git clone https://aur.archlinux.org/taskcore.git
142 | cd taskcore
143 | makepkg -si |
144 |
145 |
146 |
147 | # Want to build locally?
148 |
149 | Run the folowing command for the desired operating system. The final executable will be self-contained and single executable file. You don't need to have .NET Core runtime in the target computer where you are planning to run the published binary.
150 |
151 | ## Linux
152 | ```
153 | dotnet publish -c Release --self-contained true --runtime linux-x64 -p:PublishSingleFile=true -p:PublishTrimmed=true ./TaskCore.App/TaskCore.App.csproj
154 | ```
155 |
156 | ## Windows 10
157 | ```
158 | dotnet publish -c Release --self-contained true --runtime win10-x64 -p:PublishSingleFile=true -p:PublishTrimmed=true .\TaskCore.App\TaskCore.App.csproj
159 | ```
160 |
161 | ## MacOS
162 | ```
163 | dotnet publish -c Release --self-contained true --runtime osx-x64 -p:PublishSingleFile=true -p:PublishTrimmed=true ./TaskCore.App/TaskCore.App.csproj
164 | ```
165 |
166 | Then, go to `in/Release/netcoreapp3.1/osx-x64/publish` to locate outputted executable named `taskcore` and run the `taskcore` using the subcommands above.
167 |
168 |
--------------------------------------------------------------------------------
/TaskCore.App/IPriorityColorChooser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TaskCore.App
4 | {
5 | public interface IPriorityColorChooser
6 | {
7 | ConsoleColor GetColor(int priority);
8 | }
9 | }
--------------------------------------------------------------------------------
/TaskCore.App/Options/AddCategoryOptions.cs:
--------------------------------------------------------------------------------
1 | using CommandCore.Library.Attributes;
2 | using CommandCore.Library.PublicBase;
3 |
4 | namespace TaskCore.App.Options
5 | {
6 | public class AddCategoryOptions : VerbOptionsBase
7 | {
8 | [OptionName("title", Alias="t")]
9 | public string Title { get; set; }
10 | }
11 | }
--------------------------------------------------------------------------------
/TaskCore.App/Options/AddTaskOptions.cs:
--------------------------------------------------------------------------------
1 | using CommandCore.Library.Attributes;
2 | using CommandCore.Library.PublicBase;
3 |
4 | namespace TaskCore.App.Options
5 | {
6 | public class AddTaskOptions : VerbOptionsBase
7 | {
8 | [OptionName("title", Alias = "t", Description = "Title of the task.")]
9 | [OptionName("name", Alias = "n", Description = "Name of the task.")]
10 | public string Title { get; set; }
11 |
12 | [OptionName("duedate", Alias = "d", Description = "01/01/2010, 15(Adds 15 days from today and selects it), today, tomorrow, nextweek or \"next week\", nextmonth or \"next month\"")]
13 | public string DueDate { get; set; }
14 |
15 | [OptionName("priority", Alias = "p")]
16 | public int Priority { get; set; } = 4;
17 |
18 | [OptionName("category", Alias = "c")]
19 | public string Category { get; set; } = "Inbox";
20 |
21 | [OptionName("completed", Alias = "cm")]
22 | public bool Completed { get; set; }
23 | }
24 | }
--------------------------------------------------------------------------------
/TaskCore.App/Options/ClearDatabaseOptions.cs:
--------------------------------------------------------------------------------
1 | using CommandCore.Library.Attributes;
2 | using CommandCore.Library.PublicBase;
3 |
4 | namespace TaskCore.App.Options
5 | {
6 | public class ClearDatabaseOptions : VerbOptionsBase
7 | {
8 | [OptionName("force")]
9 | public bool Force { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/TaskCore.App/Options/CompleteTaskOptions.cs:
--------------------------------------------------------------------------------
1 | using CommandCore.Library.Attributes;
2 | using CommandCore.Library.PublicBase;
3 |
4 | namespace TaskCore.App.Options
5 | {
6 | public class CompleteTaskOptions : VerbOptionsBase
7 | {
8 | [OptionName("taskId", Alias = "i")]
9 | public int[] TaskIds { get; set; }
10 | }
11 | }
--------------------------------------------------------------------------------
/TaskCore.App/Options/DefaultOptions.cs:
--------------------------------------------------------------------------------
1 | using CommandCore.Library.PublicBase;
2 |
3 | namespace TaskCore.App.Options
4 | {
5 | public class DefaultOptions: VerbOptionsBase
6 | {
7 |
8 | }
9 | }
--------------------------------------------------------------------------------
/TaskCore.App/Options/ListTasksOptions.cs:
--------------------------------------------------------------------------------
1 | using CommandCore.Library.Attributes;
2 | using CommandCore.Library.PublicBase;
3 |
4 | namespace TaskCore.App.Options
5 | {
6 | public class ListTasksOptions: VerbOptionsBase
7 | {
8 | [OptionName("showcompleted", Alias = "c")]
9 | public bool ShowCompletedTasks { get; set; }
10 | [OptionName("verbose",Alias = "v")]
11 | public bool Verbose { get; set; }
12 | [OptionName("category", Alias="ca")]
13 | public string CategoryName { get; set; }
14 | [OptionName("priority", Alias = "p")]
15 | public int Priority { get; set; }
16 | }
17 | }
--------------------------------------------------------------------------------
/TaskCore.App/Options/RemoveCategoryOptions.cs:
--------------------------------------------------------------------------------
1 | using CommandCore.Library.Attributes;
2 | using CommandCore.Library.PublicBase;
3 |
4 | namespace TaskCore.App.Options
5 | {
6 | public class RemoveCategoryOptions : VerbOptionsBase
7 | {
8 | [OptionName("title", Alias = "t")]
9 | public string CategoryName { get; set; }
10 | }
11 | }
--------------------------------------------------------------------------------
/TaskCore.App/Options/RemoveTaskOptions.cs:
--------------------------------------------------------------------------------
1 | using CommandCore.Library.Attributes;
2 | using CommandCore.Library.PublicBase;
3 |
4 | namespace TaskCore.App.Options
5 | {
6 | public class RemoveTaskOptions : VerbOptionsBase
7 | {
8 | [OptionName("id", Alias = "i")]
9 | public int[] TaskIds { get; set; }
10 | }
11 | }
--------------------------------------------------------------------------------
/TaskCore.App/PriorityColorChooser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TaskCore.App
4 | {
5 | public class PriorityColorChooser : IPriorityColorChooser
6 | {
7 | public ConsoleColor GetColor(int priority)
8 | => priority switch
9 | {
10 | 1 => ConsoleColor.Red,
11 | 2 => ConsoleColor.Yellow,
12 | 3 => ConsoleColor.Cyan,
13 | 4 => ConsoleColor.White,
14 | _ => ConsoleColor.White,
15 | };
16 | }
17 | }
--------------------------------------------------------------------------------
/TaskCore.App/Program.cs:
--------------------------------------------------------------------------------
1 | using CommandCore.Library;
2 | using TaskCore.Dal.FileSystem;
3 | using TaskCore.Dal.Interfaces;
4 |
5 | namespace TaskCore.App
6 | {
7 | class Program
8 | {
9 | static int Main(string[] args)
10 | {
11 | var commandCoreApp = new CommandCoreApp();
12 | commandCoreApp.ConfigureServices(sp =>
13 | {
14 | sp.Register();
15 | sp.Register();
16 | sp.Register();
17 | sp.Register();
18 | });
19 |
20 | return commandCoreApp.Parse(args);
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/TaskCore.App/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "profiles": {
4 | "TaskCore.App.AddTask": {
5 | "commandName": "Project",
6 | "commandLineArgs": "add -t \"Finish up TaskCore application\" -p 1 -d 01/01/2021 -c Projects"
7 | },
8 | "TaskCore.App.AddCompletedTask": {
9 | "commandName": "Project",
10 | "commandLineArgs": "add -t \"Write your poem\" -p 2 -d 01/01/2021 -c Projects -cm"
11 | },
12 | "TaskCore.App.GenerateHelp": {
13 | "commandName": "Project",
14 | "commandLineArgs": "--help"
15 | },
16 | "TaskCore.App.ListTasks": {
17 | "commandName": "Project",
18 | "commandLineArgs": "ls"
19 | },
20 | "TaskCore.App.ListWithCompleted": {
21 | "commandName": "Project",
22 | "commandLineArgs": "ls -c"
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/TaskCore.App/TaskCore.App.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp3.1
6 | taskcore
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/TaskCore.App/Verbs/AddCategory.cs:
--------------------------------------------------------------------------------
1 | using CommandCore.Library.Attributes;
2 | using CommandCore.Library.PublicBase;
3 | using TaskCore.App.Options;
4 | using TaskCore.App.Views;
5 | using TaskCore.Dal.Interfaces;
6 | using TaskCore.Dal.Models;
7 |
8 | namespace TaskCore.App.Verbs
9 | {
10 | [VerbName("addc", Description = "Adds new category.")]
11 | public class AddCategory : VerbBase
12 | {
13 | private readonly ICategoryRepository _categoryRepository;
14 |
15 | public AddCategory(ICategoryRepository categoryRepository)
16 | {
17 | _categoryRepository = categoryRepository;
18 | }
19 |
20 | public override VerbViewBase Run()
21 | {
22 | if (_categoryRepository.GetByName(Options.Title) != null)
23 | {
24 | return new AddCategoryErrorView(Options);
25 | }
26 |
27 | _categoryRepository.Add(new Category()
28 | {
29 | Name = Options.Title
30 | });
31 |
32 | return new AddCategoryView(Options);
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/TaskCore.App/Verbs/AddTask.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using CommandCore.Library.Attributes;
4 | using CommandCore.Library.PublicBase;
5 | using TaskCore.App.Options;
6 | using TaskCore.App.Views;
7 | using TaskCore.Dal.Interfaces;
8 | using TaskCore.Dal.Models;
9 |
10 | namespace TaskCore.App.Verbs
11 | {
12 | [VerbName("add", Description = "Adds a new task.")]
13 | public class AddTask : VerbBase
14 | {
15 | private readonly ITodoTaskRepository _todoTaskRepository;
16 | private readonly ICategoryRepository _categoryRepository;
17 |
18 | public AddTask(ITodoTaskRepository todoTaskRepository, ICategoryRepository categoryRepository)
19 | {
20 | _todoTaskRepository = todoTaskRepository;
21 | _categoryRepository = categoryRepository;
22 | }
23 |
24 | public override VerbViewBase Run()
25 | {
26 | if (string.IsNullOrWhiteSpace(Options.Title))
27 | {
28 | return new AddTaskRequiredFieldsMissingView();
29 | }
30 |
31 | Category category = new Category();
32 | // Inbox is the default category, which cannot be deleted, added, or edited.
33 | if (string.IsNullOrWhiteSpace(Options.Category))
34 | {
35 | category.Name = "Inbox";
36 | }
37 | else if (_categoryRepository.GetByName(Options.Category) == null)
38 | {
39 | return new AddTaskNoCategoryView(Options.Category);
40 | }
41 | else
42 | {
43 | category.Name = Options.Category;
44 | }
45 |
46 | if (!string.IsNullOrWhiteSpace(Options.DueDate))
47 | {
48 | string loweredDueDate = Options.DueDate.ToLower();
49 |
50 | var isDueDateNumeric = int.TryParse(loweredDueDate, out _);
51 |
52 | if (isDueDateNumeric)
53 | {
54 | Options.DueDate = DateTime.Now.AddDays(int.Parse(loweredDueDate)).ToShortDateString();
55 | }
56 | else
57 | {
58 | switch (loweredDueDate)
59 | {
60 | case "today":
61 | Options.DueDate = DateTime.Now.ToShortDateString();
62 | break;
63 | case "tomorrow":
64 | Options.DueDate = DateTime.Now.AddDays(1).ToShortDateString();
65 | break;
66 | case "nextweek":
67 | case "next week":
68 | Options.DueDate = DateTime.Now.AddDays(7).ToShortDateString();
69 | break;
70 | case "nextmonth":
71 | case "next month":
72 | Options.DueDate = DateTime.Now.AddDays(30).ToShortDateString();
73 | break;
74 | default:
75 | {
76 | var isDueDateDateTime = DateTime.TryParse(loweredDueDate, out _);
77 | if (!isDueDateDateTime)
78 | {
79 | Options.DueDate = null;
80 | }
81 | break;
82 | }
83 | }
84 | }
85 | }
86 |
87 | // TODO Perform some input validation here, for required fields, etc.
88 | _todoTaskRepository.Add(new TodoTask()
89 | {
90 | Title = Options.Title,
91 | DueDateTime = Options.DueDate != null
92 | ? DateTimeOffset.Parse(Options.DueDate, CultureInfo.CurrentCulture)
93 | : (DateTimeOffset?)null,
94 | // CategoryId is a getter only hash value so we don't need to make a DB call to get it by the category name.
95 | CategoryId = category.CategoryId,
96 | Priority = Options.Priority,
97 | Completed = Options.Completed,
98 | CreationDate = DateTimeOffset.Now
99 | });
100 |
101 | return new AddTaskView(Options, category);
102 | }
103 | }
104 | }
--------------------------------------------------------------------------------
/TaskCore.App/Verbs/ClearDatabase.cs:
--------------------------------------------------------------------------------
1 | using CommandCore.Library.Attributes;
2 | using CommandCore.Library.PublicBase;
3 | using TaskCore.App.Options;
4 | using TaskCore.App.Views;
5 | using TaskCore.Dal.Interfaces;
6 |
7 | namespace TaskCore.App.Verbs
8 | {
9 | [VerbName("clear", Description = "Remove database folder.")]
10 | public class ClearDatabase : VerbBase
11 | {
12 | private readonly IDbRepository _dbRepository;
13 |
14 | public ClearDatabase(IDbRepository dbRepository)
15 | {
16 | _dbRepository = dbRepository;
17 | }
18 |
19 | public override VerbViewBase Run()
20 | {
21 | if (!Options.Force)
22 | {
23 | return new ClearDatabaseErrorView(Options);
24 | }
25 |
26 | _dbRepository.Clear();
27 | return new ClearDatabaseView(Options);
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/TaskCore.App/Verbs/CompleteTask.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using CommandCore.Library.Attributes;
3 | using CommandCore.Library.PublicBase;
4 | using TaskCore.App.Options;
5 | using TaskCore.App.Views;
6 | using TaskCore.Dal.Interfaces;
7 |
8 | namespace TaskCore.App.Verbs
9 | {
10 | [VerbName("c", Description = "Marks a given task as complete.")]
11 | public class CompleteTask : VerbBase
12 | {
13 | private readonly ITodoTaskRepository _todoTaskRepository;
14 |
15 | public CompleteTask(ITodoTaskRepository todoTaskRepository)
16 | {
17 | _todoTaskRepository = todoTaskRepository;
18 | }
19 |
20 | public override VerbViewBase Run()
21 | {
22 | // TODO By decreasing the number of query calls, the performance can be improved here, but heck yeah for now.
23 | // I will consider performance tuning in the next iteration.
24 | var tasks = _todoTaskRepository.GetActiveTasksOrderedByAddedDate();
25 | var activeTasks = tasks.Where((t, i) => Options.TaskIds.Contains(i)).ToList();
26 | foreach (var activeTask in activeTasks)
27 | {
28 | _todoTaskRepository.MarkComplete(activeTask);
29 | }
30 |
31 | return new CompleteTaskView(activeTasks);
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/TaskCore.App/Verbs/DefaultVerb.cs:
--------------------------------------------------------------------------------
1 | using CommandCore.Library.Attributes;
2 | using CommandCore.Library.PublicBase;
3 | using TaskCore.App.Options;
4 | using TaskCore.App.Views;
5 |
6 | namespace TaskCore.App.Verbs
7 | {
8 | [VerbName("default", Description = "Default ")]
9 | public class DefaultVerb: VerbBase
10 | {
11 | public override VerbViewBase Run()
12 | {
13 | return new DefaultVerbView();
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/TaskCore.App/Verbs/ListTasks.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using CommandCore.Library.Attributes;
3 | using CommandCore.Library.PublicBase;
4 | using TaskCore.App.Options;
5 | using TaskCore.App.Views;
6 | using TaskCore.Dal.Interfaces;
7 | using TaskCore.Dal.Models;
8 |
9 | namespace TaskCore.App.Verbs
10 | {
11 | [VerbName("ls", Description = "Lists all of the tasks.")]
12 | public class ListTasks : VerbBase
13 | {
14 | private readonly ITodoTaskRepository _todoTaskRepository;
15 | private readonly ICategoryRepository _categoryRepository;
16 | private readonly IPriorityColorChooser _priorityColorChooser;
17 |
18 | public ListTasks(ITodoTaskRepository todoTaskRepository,
19 | ICategoryRepository categoryRepository,
20 | IPriorityColorChooser priorityColorChooser)
21 | {
22 | _todoTaskRepository = todoTaskRepository;
23 | _categoryRepository = categoryRepository;
24 | _priorityColorChooser = priorityColorChooser;
25 | }
26 |
27 | public override VerbViewBase Run()
28 | {
29 | var activeTasks = _todoTaskRepository.GetActiveTasksOrderedByAddedDate();
30 | IReadOnlyList categories = _categoryRepository.GetAllCategories();
31 | var completedTasks = Options.ShowCompletedTasks
32 | ? _todoTaskRepository.GetCompletedTasksOrderedByAddedDate()
33 | : new List();
34 |
35 | return new ListTasksView(Options, activeTasks, completedTasks, categories, _priorityColorChooser);
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/TaskCore.App/Verbs/RemoveCategory.cs:
--------------------------------------------------------------------------------
1 | using CommandCore.Library.Attributes;
2 | using CommandCore.Library.PublicBase;
3 | using TaskCore.App.Options;
4 | using TaskCore.App.Views;
5 | using TaskCore.Dal.Interfaces;
6 | using TaskCore.Dal.Models;
7 |
8 | namespace TaskCore.App.Verbs
9 | {
10 | [VerbName("rmc", Description = "Removes a given category.")]
11 | public class RemoveCategory : VerbBase
12 | {
13 | private readonly ICategoryRepository _categoryRepository;
14 | private readonly ITodoTaskRepository _todoTaskRepository;
15 |
16 | public RemoveCategory(ICategoryRepository categoryRepository,
17 | ITodoTaskRepository todoTaskRepository)
18 | {
19 | _categoryRepository = categoryRepository;
20 | _todoTaskRepository = todoTaskRepository;
21 | }
22 |
23 | public override VerbViewBase Run()
24 | {
25 | if (Options.CategoryName.ToLower() != "inbox")
26 | {
27 | _categoryRepository.DeleteByName(Options.CategoryName);
28 | var activeTasks = _todoTaskRepository.GetActiveTasksByCategoryName(new Category()
29 | {
30 | Name = Options.CategoryName
31 | });
32 | foreach (var task in activeTasks)
33 | {
34 | _todoTaskRepository.Delete(task);
35 | }
36 | }
37 |
38 | return new RemoveCategoryView(Options);
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/TaskCore.App/Verbs/RemoveTask.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Threading.Tasks;
3 | using CommandCore.Library.Attributes;
4 | using CommandCore.Library.PublicBase;
5 | using TaskCore.App.Options;
6 | using TaskCore.App.Views;
7 | using TaskCore.Dal.Interfaces;
8 | using TaskCore.Dal.Models;
9 |
10 | namespace TaskCore.App.Verbs
11 | {
12 | [VerbName("rm", Description = "Deletes a given task.")]
13 | public class RemoveTask : VerbBase
14 | {
15 | private readonly ITodoTaskRepository _todoTaskRepository;
16 |
17 | public RemoveTask(ITodoTaskRepository todoTaskRepository)
18 | {
19 | _todoTaskRepository = todoTaskRepository;
20 | }
21 |
22 | public override VerbViewBase Run()
23 | {
24 | // For simplicity, we are using the order number of the task as the id of it. Otherwise, it would be
25 | // hard for the users to type the full id number. That's why the TaskOrder is mapped to ID even though
26 | // it is just a user construct.
27 | var activeTasks = _todoTaskRepository.GetActiveTasksOrderedByAddedDate();
28 | var toBeCompletedTasks = activeTasks.Where((a, i) => Options.TaskIds.Contains(i)).ToList();
29 | foreach (TodoTask task in toBeCompletedTasks)
30 | {
31 | _todoTaskRepository.Delete(task);
32 | }
33 | return new RemoveTaskView(toBeCompletedTasks);
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/TaskCore.App/Views/AddCategoryErrorView.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CommandCore.Library.PublicBase;
3 | using TaskCore.App.Options;
4 |
5 | namespace TaskCore.App.Views
6 | {
7 | public class AddCategoryErrorView: VerbViewBase
8 | {
9 | private readonly AddCategoryOptions _options;
10 |
11 | public AddCategoryErrorView(AddCategoryOptions options)
12 | {
13 | _options = options;
14 | }
15 | public override void RenderResponse()
16 | {
17 | Console.ForegroundColor = ConsoleColor.Red;
18 | Console.WriteLine(
19 | $"Category \"{_options.Title}\" already exists. Please use a different category name. Remember, the category names are not case sensitive.");
20 | Console.ResetColor();
21 | Environment.ExitCode = 103;
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/TaskCore.App/Views/AddCategoryView.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CommandCore.Library.PublicBase;
3 | using TaskCore.App.Options;
4 |
5 | namespace TaskCore.App.Views
6 | {
7 | public class AddCategoryView : VerbViewBase
8 | {
9 | private readonly AddCategoryOptions _options;
10 |
11 | public AddCategoryView(AddCategoryOptions options)
12 | {
13 | _options = options;
14 | }
15 |
16 | public override void RenderResponse()
17 | {
18 | Console.ForegroundColor = ConsoleColor.Green;
19 | Console.WriteLine($"Added category \"{_options.Title}\"");
20 | Console.ResetColor();
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/TaskCore.App/Views/AddTaskNoCategoryView.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CommandCore.Library.PublicBase;
3 |
4 | namespace TaskCore.App.Views
5 | {
6 | public class AddTaskNoCategoryView : VerbViewBase
7 | {
8 | private readonly string _categoryName;
9 |
10 | public AddTaskNoCategoryView(string categoryName)
11 | {
12 | _categoryName = categoryName;
13 | }
14 |
15 | public override void RenderResponse()
16 | {
17 | Console.ForegroundColor = ConsoleColor.Red;
18 | Console.WriteLine(
19 | $"Cannot find category named {_categoryName}. Either add it first or leave it empty so that default category \"Inbox\" can be used");
20 | Console.ResetColor();
21 | Environment.ExitCode = 100;
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/TaskCore.App/Views/AddTaskRequiredFieldsMissingView.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CommandCore.Library.PublicBase;
3 |
4 | namespace TaskCore.App.Views
5 | {
6 | public class AddTaskRequiredFieldsMissingView: VerbViewBase
7 | {
8 | public override void RenderResponse()
9 | {
10 | Console.ForegroundColor = ConsoleColor.Red;
11 | Console.WriteLine("Task title is required. Please enter one to create a new task item.");
12 | Console.ResetColor();
13 | Environment.ExitCode = 102;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/TaskCore.App/Views/AddTaskView.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CommandCore.Library.PublicBase;
3 | using TaskCore.App.Options;
4 | using TaskCore.Dal.Models;
5 | using static System.Console;
6 |
7 | namespace TaskCore.App.Views
8 | {
9 | public class AddTaskView : VerbViewBase
10 | {
11 | private readonly AddTaskOptions _options;
12 | private readonly Category _category;
13 |
14 | public AddTaskView(AddTaskOptions options, Category category)
15 | {
16 | _options = options;
17 | _category = category;
18 | }
19 |
20 | public override void RenderResponse()
21 | {
22 | ForegroundColor = ConsoleColor.Green;
23 | WriteLine($"Added the task \"{_options.Title}\" to {_category.Name}");
24 | ResetColor();
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/TaskCore.App/Views/ClearDatabaseErrorView.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CommandCore.Library.PublicBase;
3 | using TaskCore.App.Options;
4 |
5 | namespace TaskCore.App.Views
6 | {
7 | public class ClearDatabaseErrorView: VerbViewBase
8 | {
9 | private readonly ClearDatabaseOptions _options;
10 |
11 | public ClearDatabaseErrorView(ClearDatabaseOptions options)
12 | {
13 | _options = options;
14 | }
15 |
16 | public override void RenderResponse()
17 | {
18 | Console.ForegroundColor = ConsoleColor.Red;
19 | Console.WriteLine(
20 | "If you're sure you want to delete the database folder, rerun this command with --force.");
21 | Console.ResetColor();
22 | Environment.ExitCode = 105;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/TaskCore.App/Views/ClearDatabaseView.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CommandCore.Library.PublicBase;
3 | using TaskCore.App.Options;
4 |
5 | namespace TaskCore.App.Views
6 | {
7 | public class ClearDatabaseView : VerbViewBase
8 | {
9 | private readonly ClearDatabaseOptions _options;
10 |
11 | public ClearDatabaseView(ClearDatabaseOptions options)
12 | {
13 | _options = options;
14 | }
15 |
16 | public override void RenderResponse()
17 | {
18 | Console.ForegroundColor = ConsoleColor.Green;
19 | Console.WriteLine($"Database folder removed.");
20 | Console.ResetColor();
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/TaskCore.App/Views/CompleteTaskView.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using CommandCore.Library.PublicBase;
4 | using TaskCore.Dal.Models;
5 |
6 | namespace TaskCore.App.Views
7 | {
8 | public class CompleteTaskView : VerbViewBase
9 | {
10 | private readonly List _tasks;
11 |
12 | public CompleteTaskView(List tasks)
13 | {
14 | _tasks = tasks;
15 | }
16 |
17 | public override void RenderResponse()
18 | {
19 | foreach (var task in _tasks)
20 | {
21 | Console.WriteLine($"Completed the task \"{task.Title}\"");
22 | }
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/TaskCore.App/Views/DefaultVerbView.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CommandCore.Library.PublicBase;
3 |
4 | namespace TaskCore.App.Views
5 | {
6 | public class DefaultVerbView: VerbViewBase
7 | {
8 | public override void RenderResponse()
9 | {
10 | Console.WriteLine("Please use --help to find out which commands are available.");
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/TaskCore.App/Views/ListTasksView.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using CommandCore.Library.PublicBase;
5 | using TaskCore.App.Options;
6 | using TaskCore.Dal.Models;
7 | using static System.Console;
8 |
9 | namespace TaskCore.App.Views
10 | {
11 | public class ListTasksView : VerbViewBase
12 | {
13 | private readonly ListTasksOptions _options;
14 | private readonly IReadOnlyList _activeTasks;
15 | private readonly IReadOnlyList _completedTasks;
16 | private readonly Dictionary _categoriesDict;
17 | private readonly IPriorityColorChooser _priorityColorChooser;
18 |
19 | public ListTasksView(ListTasksOptions options, IReadOnlyList activeTasks,
20 | IReadOnlyList completedTasks, IReadOnlyList categories,
21 | IPriorityColorChooser priorityColorChooser)
22 | {
23 | _options = options;
24 | _activeTasks = activeTasks;
25 | _completedTasks = completedTasks;
26 | _categoriesDict = categories.ToDictionary(a => a.CategoryId, a => a.Name)!;
27 | _priorityColorChooser = priorityColorChooser;
28 | }
29 |
30 | public override void RenderResponse()
31 | {
32 | ForegroundColor = ConsoleColor.Blue;
33 | WriteLine($"[ ] ACTIVE TASKS - Total #: {_activeTasks.Count}");
34 | WriteLine("--------------------------------");
35 | ResetColor();
36 |
37 | RenderActiveTasks();
38 |
39 | if (_completedTasks.Count > 0)
40 | {
41 | WriteLine();
42 | ForegroundColor = ConsoleColor.Green;
43 | WriteLine($"[X] COMPLETED TASKS - Total #: {_completedTasks.Count}");
44 | WriteLine("----------------------------------");
45 | ResetColor();
46 | RenderCompletedTasks();
47 | }
48 | }
49 |
50 |
51 | private void WarnForEmptyResult(bool isForCompletedTasks = false)
52 | {
53 | var taskType = isForCompletedTasks ? "completed" : "active";
54 |
55 | ForegroundColor = ConsoleColor.Red;
56 | if (!string.IsNullOrWhiteSpace(_options.CategoryName) && _options.Priority != 0)
57 | {
58 | Write("There is no {0} task with the priority {1} and the Category name: {2}", taskType, _options.Priority, _options.CategoryName);
59 | }
60 | else if (_options.Priority != 0)
61 | {
62 | Write("There is no {0} task with the priority {1}", taskType, _options.Priority);
63 | }
64 | else if (!string.IsNullOrWhiteSpace(_options.CategoryName))
65 | {
66 | Write("There is no {0} task with the Category name: {1}", taskType, _options.CategoryName);
67 | }
68 | else
69 | {
70 | Write("There's no {0} task", taskType);
71 | }
72 | ResetColor();;
73 | }
74 |
75 | private void RenderCompletedTasks()
76 | {
77 | var completedTask = _completedTasks.AsEnumerable();
78 | if (!string.IsNullOrWhiteSpace(_options.CategoryName))
79 | {
80 | var categoryId = new Category() { Name = _options.CategoryName }.CategoryId;
81 | completedTask = completedTask.Where(a => a.CategoryId == categoryId);
82 | }
83 | if (_options.Priority != 0)
84 | {
85 | completedTask = completedTask.Where(x => x.Priority == _options.Priority).ToList();
86 | }
87 | if (!completedTask.Any())
88 | {
89 | WarnForEmptyResult(true);
90 | }
91 |
92 | foreach (var task in completedTask)
93 | {
94 | Write($"- [X] \"{task.Title}\" P{task.Priority} in {_categoriesDict[task.CategoryId]}");
95 | if (_options.Verbose)
96 | {
97 | Write($" - Created at {task.CreationDate:F}, completed at {task.CompletionDate:F}");
98 | }
99 |
100 | WriteLine();
101 | }
102 | }
103 |
104 | private void RenderActiveTasks()
105 | {
106 | var activeTasks = _activeTasks;
107 | if (!string.IsNullOrWhiteSpace(_options.CategoryName))
108 | {
109 | var categoryId = new Category() { Name = _options.CategoryName }.CategoryId;
110 | activeTasks = activeTasks.Where(x => x.CategoryId == categoryId).ToList();
111 | }
112 | if (_options.Priority != 0)
113 | {
114 | activeTasks = activeTasks.Where(x => x.Priority == _options.Priority).ToList();
115 | }
116 | if (!activeTasks.Any())
117 | {
118 | WarnForEmptyResult();
119 | }
120 |
121 | for (var i = 0; i < activeTasks.Count; i++)
122 | {
123 | var task = activeTasks[i];
124 |
125 | var usePriorityColor = task.Priority > 0 && task.Priority < 4;
126 | if (usePriorityColor)
127 | {
128 | ForegroundColor = _priorityColorChooser.GetColor(task.Priority);
129 | }
130 |
131 | var completed = task.Completed ? "X" : " ";
132 | Write($"[{completed}]");
133 | ResetColor();
134 | Write(" ");
135 | Write($"{i}. \"{task.Title}\"");
136 | Write(" ");
137 |
138 | if (usePriorityColor)
139 | {
140 | ForegroundColor = _priorityColorChooser.GetColor(task.Priority);
141 | }
142 |
143 | Write($"P{task.Priority}");
144 | ResetColor();
145 | Write(" ");
146 | Write($"in {_categoriesDict[task.CategoryId]}");
147 | if (_options.Verbose)
148 | {
149 | Write($" - Created on {task.CreationDate:F}.");
150 | Write($" - Due Date on {task.DueDateTime:F}");
151 | }
152 |
153 | WriteLine();
154 | }
155 | }
156 | }
157 | }
--------------------------------------------------------------------------------
/TaskCore.App/Views/RemoveCategoryView.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CommandCore.Library.PublicBase;
3 | using TaskCore.App.Options;
4 |
5 | namespace TaskCore.App.Views
6 | {
7 | public class RemoveCategoryView : VerbViewBase
8 | {
9 | private readonly RemoveCategoryOptions _options;
10 |
11 | public RemoveCategoryView(RemoveCategoryOptions options)
12 | {
13 | _options = options;
14 | }
15 |
16 | public override void RenderResponse()
17 | {
18 | if (_options.CategoryName.ToLower() == "inbox")
19 | {
20 | Console.ForegroundColor = ConsoleColor.Red;
21 | Console.WriteLine($"Inbox category may not be deleted as it is the default category that always exists!");
22 | Console.ResetColor();;
23 | }
24 | Console.ForegroundColor = ConsoleColor.Blue;
25 | Console.WriteLine($"Deleted category \"{_options.CategoryName}\"");
26 | Console.ResetColor();
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/TaskCore.App/Views/RemoveTaskView.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using CommandCore.Library.PublicBase;
4 | using TaskCore.Dal.Models;
5 |
6 | namespace TaskCore.App.Views
7 | {
8 | public class RemoveTaskView: VerbViewBase
9 | {
10 | private readonly List _tasks;
11 |
12 | public RemoveTaskView(List tasks)
13 | {
14 | _tasks = tasks;
15 | }
16 | public override void RenderResponse()
17 | {
18 | Console.ForegroundColor = ConsoleColor.Red;
19 | foreach (var task in _tasks)
20 | {
21 | Console.WriteLine($"Deleted task \"{task.Title}\"");
22 | }
23 | Console.ResetColor();
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/TaskCore.Dal.FileSystem/CategoryRepository.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Linq;
4 | using Newtonsoft.Json;
5 | using Newtonsoft.Json.Linq;
6 | using TaskCore.Dal.Interfaces;
7 | using TaskCore.Dal.Models;
8 |
9 | namespace TaskCore.Dal.FileSystem
10 | {
11 | public class CategoryRepository : ICategoryRepository
12 | {
13 | private readonly FileManager _fileManager;
14 |
15 | public CategoryRepository()
16 | {
17 | _fileManager = new FileManager();
18 | }
19 |
20 | public Category? GetByName(string categoryName)
21 | {
22 | if (categoryName.ToLower() == "inbox")
23 | {
24 | return new Category() {Name = "Inbox"};
25 | }
26 |
27 | var content = _fileManager.GetCategoryFile(new Category() {Name = categoryName}.CategoryId);
28 | return content == null
29 | ? null
30 | : new JsonSerializer().Deserialize(new JsonTextReader(new StringReader(content)));
31 | }
32 |
33 | public Category Add(Category category)
34 | {
35 | var content = JObject.FromObject(category);
36 | _fileManager.SaveCategoryFile(category.CategoryId, content.ToString());
37 | return category;
38 | }
39 |
40 | public bool DeleteByName(string categoryName)
41 | {
42 | _fileManager.DeleteCategoryFile(new Category() {Name = categoryName}.CategoryId);
43 | return true;
44 | }
45 |
46 | public IReadOnlyList GetAllCategories()
47 | {
48 | List categoryFiles = _fileManager.GetAlCategoryFiles();
49 | var categories = categoryFiles.Select(a =>
50 | new JsonSerializer()
51 | .Deserialize(new JsonTextReader(new StringReader(a)))).ToList();
52 | categories.Add(new Category() {Name = "Inbox"});
53 | return categories;
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/TaskCore.Dal.FileSystem/DbRepository.cs:
--------------------------------------------------------------------------------
1 | using TaskCore.Dal.Interfaces;
2 |
3 | namespace TaskCore.Dal.FileSystem
4 | {
5 | public class DbRepository : IDbRepository
6 | {
7 | private readonly FileManager _fileManager;
8 |
9 | public DbRepository()
10 | {
11 | _fileManager = new FileManager();
12 | }
13 |
14 | public void Clear()
15 | {
16 | _fileManager.RemoveDbFolder();
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/TaskCore.Dal.FileSystem/FileManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 |
6 | namespace TaskCore.Dal.FileSystem
7 | {
8 | internal class FileManager
9 | {
10 | private readonly DirectoryInfo _activeTasksDir;
11 | private readonly DirectoryInfo _completedTasksDir;
12 | private readonly DirectoryInfo _categoriesDir;
13 |
14 | public FileManager()
15 | {
16 | string dbPath = GetDbPath();
17 | if (!Directory.Exists(dbPath))
18 | {
19 | Directory.CreateDirectory(dbPath);
20 | Directory.CreateDirectory(Path.Combine(dbPath, "active"));
21 | Directory.CreateDirectory(Path.Combine(dbPath, "completed"));
22 | Directory.CreateDirectory(Path.Combine(dbPath, "categories"));
23 | }
24 |
25 | _activeTasksDir = new DirectoryInfo(Path.Combine(dbPath, "active"));
26 | _completedTasksDir = new DirectoryInfo(Path.Combine(dbPath, "completed"));
27 | _categoriesDir = new DirectoryInfo(Path.Combine(dbPath, "categories"));
28 | }
29 |
30 | static string GetDbPath()
31 | {
32 | string dbPath = Environment.GetEnvironmentVariable("TASKCORE_DB_PATH") ?? string.Empty;
33 | return string.IsNullOrWhiteSpace(dbPath)
34 | ? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "taskcore")
35 | : dbPath;
36 | }
37 |
38 | public void RemoveDbFolder()
39 | {
40 | Directory.Delete(GetDbPath(), true);
41 | }
42 |
43 | public void SaveActiveTask(string fileName, string content)
44 | {
45 | File.WriteAllText(Path.Combine(_activeTasksDir.FullName, fileName),
46 | content);
47 | }
48 |
49 | public void SaveCompletedTask(string fileName, string content)
50 | {
51 | File.WriteAllText(Path.Combine(_completedTasksDir.FullName, fileName),
52 | content);
53 | }
54 |
55 | public void DeleteActiveTask(string fileName)
56 | {
57 | File.Delete(Path.Combine(_activeTasksDir.FullName, fileName));
58 | }
59 |
60 | public void DeleteCompletedTask(string fileName)
61 | {
62 | File.Delete(Path.Combine(_completedTasksDir.FullName, fileName));
63 | }
64 |
65 | public List GetActiveTasksContents()
66 | {
67 | var allTaskFiles = _activeTasksDir.GetFiles();
68 | return allTaskFiles.Select(a => File.ReadAllText(a.FullName)).ToList();
69 | }
70 |
71 | public List GetCompletedTasksContents()
72 | {
73 | var allTaskFiles = _completedTasksDir.GetFiles();
74 | return allTaskFiles.Select(a => File.ReadAllText(a.FullName)).ToList();
75 | }
76 |
77 | public void SaveCategoryFile(string fileName, string content)
78 | {
79 | File.WriteAllText(Path.Combine(_categoriesDir.FullName, fileName), content);
80 | }
81 |
82 | public void DeleteCategoryFile(string fileName)
83 | {
84 | var fileAbsolutePath = Path.Combine(_categoriesDir.FullName, fileName);
85 | if (File.Exists(fileAbsolutePath))
86 | {
87 | File.Delete(fileAbsolutePath);
88 | }
89 | }
90 |
91 | public string? GetCategoryFile(string fileName)
92 | {
93 | var fileAbsolutePath = Path.Combine(_categoriesDir.FullName, fileName);
94 | return File.Exists(fileAbsolutePath) ? File.ReadAllText(fileAbsolutePath) : null;
95 | }
96 |
97 | public List GetAlCategoryFiles()
98 | {
99 | var files = _categoriesDir.GetFiles();
100 | return files.Select(a => File.ReadAllText(a.FullName)).ToList();
101 | }
102 | }
103 | }
--------------------------------------------------------------------------------
/TaskCore.Dal.FileSystem/TaskCore.Dal.FileSystem.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | enable
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/TaskCore.Dal.FileSystem/TaskFileName.cs:
--------------------------------------------------------------------------------
1 | namespace TaskCore.Dal.FileSystem
2 | {
3 | internal class TaskFileName
4 | {
5 | public long TaskId { get; set; }
6 | public bool Completed { get; set; }
7 | }
8 | }
--------------------------------------------------------------------------------
/TaskCore.Dal.FileSystem/TodoTaskRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using Newtonsoft.Json;
6 | using Newtonsoft.Json.Linq;
7 | using TaskCore.Dal.Interfaces;
8 | using TaskCore.Dal.Models;
9 | using JsonSerializer = Newtonsoft.Json.JsonSerializer;
10 |
11 | namespace TaskCore.Dal.FileSystem
12 | {
13 | public class TodoTaskRepository : ITodoTaskRepository
14 | {
15 | private readonly FileManager _fileManager;
16 |
17 | public TodoTaskRepository()
18 | {
19 | _fileManager = new FileManager();
20 | }
21 |
22 | public IReadOnlyList GetActiveTasksOrderedByAddedDate()
23 | {
24 | var activeTasks = _fileManager.GetActiveTasksContents();
25 | return activeTasks
26 | .Select(a => JsonSerializer.Create()
27 | .Deserialize(new JsonTextReader(new StringReader(a))))
28 | .OrderByDescending(a => a.Id)
29 | .ToList();
30 | }
31 |
32 | public IReadOnlyList GetCompletedTasksOrderedByAddedDate(bool includeCompletedTasks = false)
33 | {
34 | var activeTasks = _fileManager.GetCompletedTasksContents();
35 | return activeTasks
36 | .Select(a => JsonSerializer.Create()
37 | .Deserialize(new JsonTextReader(new StringReader(a))))
38 | .OrderByDescending(a => a.Id)
39 | .ToList();
40 | }
41 |
42 | public void Update(TodoTask task)
43 | {
44 | if (task.Completed)
45 | {
46 | _fileManager.SaveCompletedTask(task.Id.ToString(),
47 | JObject.FromObject(task).ToString());
48 | }
49 | else
50 | {
51 | _fileManager.SaveActiveTask(task.Id.ToString(),
52 | JObject.FromObject(task).ToString());
53 | }
54 | }
55 |
56 | public void MarkComplete(TodoTask task)
57 | {
58 | task.Completed = true;
59 | task.CompletionDate = DateTimeOffset.Now;
60 | _fileManager.DeleteActiveTask(task.Id.ToString());
61 | _fileManager.SaveCompletedTask(task.Id.ToString(), JObject.FromObject(task).ToString());
62 | }
63 |
64 | public IReadOnlyList GetActiveTasksByCategoryName(Category category)
65 | {
66 | var allActiveTasks = GetActiveTasksOrderedByAddedDate();
67 | return allActiveTasks.Where(a => a.CategoryId == category.CategoryId).ToList();
68 | }
69 |
70 | public void Add(TodoTask task)
71 | {
72 | var jTask = JObject.FromObject(task);
73 | var timestamp = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
74 | jTask["Id"] = timestamp;
75 | _fileManager.SaveActiveTask(timestamp.ToString(), jTask.ToString());
76 | }
77 |
78 | public void Delete(TodoTask task)
79 | {
80 | if (task.Completed)
81 | {
82 | _fileManager.DeleteCompletedTask(task.Id.ToString());
83 | }
84 | else
85 | {
86 | _fileManager.DeleteActiveTask(task.Id.ToString());
87 | }
88 | }
89 | }
90 | }
--------------------------------------------------------------------------------
/TaskCore.Dal.Interfaces/ICategoryRepository.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using TaskCore.Dal.Models;
3 |
4 | namespace TaskCore.Dal.Interfaces
5 | {
6 | public interface ICategoryRepository
7 | {
8 | Category? GetByName(string categoryName);
9 |
10 | ///
11 | /// Creates a new category and returns a category object with Id.
12 | ///
13 | Category Add(Category category);
14 |
15 | ///
16 | /// Deletes a category and returns true if the deletion was a success.
17 | ///
18 | bool DeleteByName(string categoryName);
19 |
20 | IReadOnlyList GetAllCategories();
21 | }
22 | }
--------------------------------------------------------------------------------
/TaskCore.Dal.Interfaces/IDbRepository.cs:
--------------------------------------------------------------------------------
1 | namespace TaskCore.Dal.Interfaces
2 | {
3 | public interface IDbRepository
4 | {
5 | void Clear();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/TaskCore.Dal.Interfaces/ITodoTaskRepository.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using TaskCore.Dal.Models;
3 |
4 | namespace TaskCore.Dal.Interfaces
5 | {
6 | public interface ITodoTaskRepository
7 | {
8 | IReadOnlyList GetActiveTasksOrderedByAddedDate();
9 | IReadOnlyList GetCompletedTasksOrderedByAddedDate(bool includeCompletedTasks = false);
10 | void Update(TodoTask task);
11 | IReadOnlyList GetActiveTasksByCategoryName(Category category);
12 | void Add(TodoTask task);
13 | void Delete(TodoTask task);
14 | void MarkComplete(TodoTask task);
15 | }
16 | }
--------------------------------------------------------------------------------
/TaskCore.Dal.Interfaces/TaskCore.Dal.Interfaces.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | enable
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/TaskCore.Dal.Models/Category.cs:
--------------------------------------------------------------------------------
1 | namespace TaskCore.Dal.Models
2 | {
3 | public class Category
4 | {
5 | public string CategoryId => GenerateHash(Name!.ToLower()).ToString();
6 | public string? Name { get; set; }
7 | private static int GenerateHash(string str)
8 | {
9 | unchecked
10 | {
11 | int hash1 = (5381 << 16) + 5381;
12 | int hash2 = hash1;
13 |
14 | for (int i = 0; i < str.Length; i += 2)
15 | {
16 | hash1 = ((hash1 << 5) + hash1) ^ str[i];
17 | if (i == str.Length - 1)
18 | break;
19 | hash2 = ((hash2 << 5) + hash2) ^ str[i + 1];
20 | }
21 |
22 | return hash1 + (hash2 * 1566083941);
23 | }
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/TaskCore.Dal.Models/TaskCore.Dal.Models.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | enable
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/TaskCore.Dal.Models/TodoTask.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TaskCore.Dal.Models
4 | {
5 | public class TodoTask
6 | {
7 | public long Id { get; set; }
8 | public string? Title { get; set; }
9 | public DateTimeOffset? DueDateTime { get; set; }
10 | public int Priority { get; set; }
11 | public bool Completed { get; set; }
12 | public string? CategoryId { get; set; }
13 | public DateTimeOffset CreationDate { get; set; }
14 | public DateTimeOffset? CompletionDate { get; set; }
15 |
16 | }
17 | }
--------------------------------------------------------------------------------
/TaskCore.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TaskCore.App", "TaskCore.App\TaskCore.App.csproj", "{4C5D53C9-99A1-4341-B21F-35527B70F871}"
4 | EndProject
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TaskCore.Dal.Interfaces", "TaskCore.Dal.Interfaces\TaskCore.Dal.Interfaces.csproj", "{3663A786-DC18-42D5-B6AE-534514B7DFE8}"
6 | EndProject
7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TaskCore.Dal.FileSystem", "TaskCore.Dal.FileSystem\TaskCore.Dal.FileSystem.csproj", "{6B61069E-D884-4F7F-81B0-87E8DF557E34}"
8 | EndProject
9 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TaskCore.Dal.Models", "TaskCore.Dal.Models\TaskCore.Dal.Models.csproj", "{F0E27299-97B9-4A28-9D65-6E43D63C7962}"
10 | EndProject
11 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dal", "Dal", "{DF8B1581-7D8B-4B5A-9D6D-801B8A27F512}"
12 | EndProject
13 | Global
14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
15 | Debug|Any CPU = Debug|Any CPU
16 | Release|Any CPU = Release|Any CPU
17 | EndGlobalSection
18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
19 | {4C5D53C9-99A1-4341-B21F-35527B70F871}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20 | {4C5D53C9-99A1-4341-B21F-35527B70F871}.Debug|Any CPU.Build.0 = Debug|Any CPU
21 | {4C5D53C9-99A1-4341-B21F-35527B70F871}.Release|Any CPU.ActiveCfg = Release|Any CPU
22 | {4C5D53C9-99A1-4341-B21F-35527B70F871}.Release|Any CPU.Build.0 = Release|Any CPU
23 | {3663A786-DC18-42D5-B6AE-534514B7DFE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
24 | {3663A786-DC18-42D5-B6AE-534514B7DFE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
25 | {3663A786-DC18-42D5-B6AE-534514B7DFE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
26 | {3663A786-DC18-42D5-B6AE-534514B7DFE8}.Release|Any CPU.Build.0 = Release|Any CPU
27 | {6B61069E-D884-4F7F-81B0-87E8DF557E34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
28 | {6B61069E-D884-4F7F-81B0-87E8DF557E34}.Debug|Any CPU.Build.0 = Debug|Any CPU
29 | {6B61069E-D884-4F7F-81B0-87E8DF557E34}.Release|Any CPU.ActiveCfg = Release|Any CPU
30 | {6B61069E-D884-4F7F-81B0-87E8DF557E34}.Release|Any CPU.Build.0 = Release|Any CPU
31 | {F0E27299-97B9-4A28-9D65-6E43D63C7962}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
32 | {F0E27299-97B9-4A28-9D65-6E43D63C7962}.Debug|Any CPU.Build.0 = Debug|Any CPU
33 | {F0E27299-97B9-4A28-9D65-6E43D63C7962}.Release|Any CPU.ActiveCfg = Release|Any CPU
34 | {F0E27299-97B9-4A28-9D65-6E43D63C7962}.Release|Any CPU.Build.0 = Release|Any CPU
35 | EndGlobalSection
36 | GlobalSection(NestedProjects) = preSolution
37 | {6B61069E-D884-4F7F-81B0-87E8DF557E34} = {DF8B1581-7D8B-4B5A-9D6D-801B8A27F512}
38 | {3663A786-DC18-42D5-B6AE-534514B7DFE8} = {DF8B1581-7D8B-4B5A-9D6D-801B8A27F512}
39 | {F0E27299-97B9-4A28-9D65-6E43D63C7962} = {DF8B1581-7D8B-4B5A-9D6D-801B8A27F512}
40 | EndGlobalSection
41 | EndGlobal
42 |
--------------------------------------------------------------------------------
/assests/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tarikguney/taskcore/42f88c12b37c9375693156e16664439d68d10049/assests/banner.png
--------------------------------------------------------------------------------
/taskcore_demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tarikguney/taskcore/42f88c12b37c9375693156e16664439d68d10049/taskcore_demo.gif
--------------------------------------------------------------------------------