I'm Katherine, or Kat, or Katie!
- 📫 How to reach me: [email protected]
- 😄 Pronouns: she/they
- 🔭 I’m currently working on my PRNG, my cryptography library and my app, Sobriety
A simple and minimal app to track how long you've been sober from anything you want.
License: GNU General Public License v3.0
I'm Katherine, or Kat, or Katie!
When adding a new entry, I think the recent average should be null instead of the current implementation.
If I add an entry with a future time, it shows the recent average as the difference between now and the set time (in negative). Same applies to adding an entry with a past time (difference in positive).
Your ReadMe doesn't mention contribution guidelines, so im curious if you are open to PRs.
I think your app is a cool idea so I forked your project and made some minor changes like adding confirmation dialogs for the delete / reset actions etc.
Hi. This is coming along nicely. I found a bug though.
If I turn my phone and it switches from portrait to landscape (or vice versa) with the app open and in the foreground, a duplicate set of fully functional cards will appear. The screen shot below was taken after turning the phone twice (from portrait to landscape, and then back to portrait). So you see two duplicate sets.
The duplicate cards will disappear if I manually swipe away the app from my recents or force close it from my app settings; Unless I interact with one of the cards by the relapse or delete button. If I do that, all the duplicates become permanent.
I tested with 1 and then 2 addiction cards. I'm using a OnePlus 8 fully stock running Android 12.
Unfortunately, the App from F-Droid is not compatible with a Huawei P40 Lite E (cannot install).
Does it requires Google Services ? Is there any way to use it without Google services ?
Thanks for your great project and for using a GPLv3 License ! :)
Hey, what a wonderful app and so helpful !!
Thanks a lot !
I really appreciate the app itself and the update that were brought.
I was wondering : in the savings dashboard of each addiction, would it be possible to have the total sum calculated and showed as a result ?
Like for example 10min saved per day, under this line after one week it would show 70min saved.
I find it could be really motivating to have it as an overview.
Thanks again for taking the initiative to make this app.
Helps a lot of people, me inluded ;)
Basically, relapsing would request text input and keep that note in the timeline so that you can see what happened
I am having trouble reading red on purple. It would be great to have a more standard neutral background like white and dark grey. With Material You it could of use those colors.
I hoped this app would have a widget but I didn't see one at all. I think this makes the most sense as opening your phone, seeing the widget right there would be motivating and helpful.
#106 is a great to have additional information in the app. A link to the github repository would be great as well.
Are there any limitations that make this app incompatible with older Android devices? Can't install it from provided APK files.
I think it can be easily lowered to 21.
https://github.com/KiARC/Sobriety/blob/master/app/build.gradle#L11
I just updated to v8.0.0 from v7.0.0 and now I can't even open the app. All is does is crash on me.
To clarify, my device is a Samsung Galaxy A23 5G (it's actually LTE). Specific model number is SM-A236U.
It's running android 12.
The milestones and savings disappear when closing the milestones and savings view.
Here is a Youtube Short of the bug https://youtu.be/0iBE2V1FUvA
Don't ask me why a short; Youtube decided it was a short.
I am running the latest version available on F-Droid (just updated and tested before posting the issue).
Just updated via F-Droid and got this error when attempting to launch the app.
type: crash
osVersion: google/oriole/oriole:13/TP1A.220905.004.A1/2022091400:user/release-keys
package: com.katiearose.sobriety:12
process: com.katiearose.sobriety
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.katiearose.sobriety/com.katiearose.sobriety.Main}: java.io.InvalidClassException: m1.a; class invalid for deserialization
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3677)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3814)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:101)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2309)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7904)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ExecInit.main(ExecInit.java:49)
at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:355)
Caused by: java.io.InvalidClassException: m1.a; class invalid for deserialization
at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:154)
at java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:798)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1901)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1440)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:428)
at java.util.ArrayList.readObject(ArrayList.java:791)
at java.lang.reflect.Method.invoke(Native Method)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1066)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2041)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1927)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1440)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:428)
at n1.e.a(Unknown Source:105)
at com.katiearose.sobriety.Main.onCreate(Unknown Source:88)
at android.app.Activity.performCreate(Activity.java:8301)
at android.app.Activity.performCreate(Activity.java:8280)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1389)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3658)
... 14 more
Afaik the "recent average" function calculates the average of the last 3 abstaining periods. With that in mind, I'd like to report some, uh, weird behavior with it:
Example: If I create an addiction with the time of 1 hour ago and I log the relapse, the average is displayed as 20 minutes (which is correct). However, if I close and open the app again, if the time of the new period is 10 seconds, when I open the app, the "average" is displayed as 10 seconds instead of the actual average value.
Am i gonna fix it?
Not All the Tasks or Addictions are required to be tracked by Hours , Minutes and Seconds . It will be Clutter Free If Only years , Months and Days are Displayed for a particular Tasks. Sometimes Minutes and Seconds are distracting if we have Lot of tasks added.
I try and download but Permissions aren't met.?
I am 100% sure they worked before but now on every release on multiple devices the cached times don't reset correctly
Expected: Reset time, close and open app, time is relatively small because of the recent reset
Actual: Reset time, close and open app, time is back where it was
I am genuinely baffled.
I used to have one thing in my addiction list
(Fresh new install from fdroid today) and a half hour later when i looked again the list has doubled to two duplicate addictions..
This happened a few times
I recently added another item to my list and only a few minutes later the list has doubled again to 4 items both items both duplicated.
Having a simple history of abstaining from something would be nice. Even more so if you implement the stop\restart idea.
I think it would be useful to log how long you were sober for and display an average (possibly of the last 3 resets) meaning users could aim for more (or less) time sober from something
v8.0.0 update crashes immediately upon opening on Pixel 6 Pro running Graphene OS
I've been using Sobriety for a while now ad really enjoy it. There are some UI changes I'd like to suggest though so I created some mockups:
Sobriety | Mockup |
---|---|
Sobriety | Mockup |
---|---|
I used the standard MD3 cards to improve readability. I used just a day counter, since it looks a lot cleaner imo. I also think it's more motivating and easier to comprehend if you see 117 days instead of 3 months, 3 weeks, 6 days, 11 hours, 38 minutes and 41 seconds. I put more emphasis on the reset
button and added an expand
button. Pressing that button would expand the card with animatedVisibility
. This cleans up the UI a lot and moves all the buttons in one row where they are easily accessible. The delete
button is missing because I'd suggest using swipeToDismiss
, but it could be added in the row as well. These changes would close #109, #103 and #102. Unfortunately don't know html. I've only used compose so far, or I would start working on some of these suggestions. If they are welcome that is... :)
Basically the title. Just FYI, your IzzyOnDroid icon links to a bigger image instead of Izzy's repo.
https://apt.izzysoft.de/fdroid/index/apk/com.katiearose.sobriety
Can you provide me a backgroundless version of the app icon so i can do this?
Upgraded from the F-Droid 6.2.1 release and the App crashed on launch.
The App cache was already empty and I don't want to clear the actual data and set it up again 😒 .
Here is the startup exception:
11-23 16:33:54.950 15702 15702 E AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.katiearose.sobriety/com.katiearose.sobriety.activities.Main}: java.io.InvalidClassException: j$.time.t; class invalid for deserialization
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3280)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3419)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2026)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:107)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at android.os.Looper.loop(Looper.java:214)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:7400)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:491)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: Caused by: java.io.InvalidClassException: j$.time.t; class invalid for deserialization
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:154)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:798)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1873)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1412)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at java.io.ObjectInputStream.readObject(ObjectInputStream.java:427)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at java.util.HashMap.readObject(HashMap.java:1408)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1066)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2013)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1899)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1412)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at java.io.ObjectInputStream.readObject(ObjectInputStream.java:427)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at java.util.ArrayList.readObject(ArrayList.java:791)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1066)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2013)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1899)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1412)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at java.io.ObjectInputStream.readObject(ObjectInputStream.java:427)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at j1.a.a(Unknown Source:105)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at com.katiearose.sobriety.activities.Main.onCreate(Unknown Source:148)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at android.app.Activity.performCreate(Activity.java:7824)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at android.app.Activity.performCreate(Activity.java:7813)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1307)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3255)
11-23 16:33:54.950 15702 15702 E AndroidRuntime: ... 11 more
Maybe this issue is related to #48 , but I'm wondering why it's not mentioned in the release notes (or fixed before the F-Droid release), as the Issue seems to be known for about a month.
I'll debug it later when I have time
When scrolling down the "plus" button for adding a new tracker covers the button to reset the entry lowest on the list of trackers.
A workaround seems to be adding a new tracker, named Zzzz or something similar, with low priority. As such its not a breaking change. It would be nice to make the "plus button optional in the settings though :)
Does this app follow the calendar year or does it just accumulate seconds and minutes to form weeks and months? The app says im 11 months 3 weeks, but by the calendar im 1 year 4 days?
Unfortunately this bug seems to keep reappearing😄 The clean install of 5.1.4 works perfectly fine (also thanks for crediting me in the release notes) but when updating from 4.0.1 with an existing save file it crashes again.
However this time its reproducible in the Emulator, so maybe that helps in getting rid of the bug.
Steps to reproduce:
I appended the full crash log at the end, but the only relevant lines are
Caused by: java.io.InvalidClassException: com.katiearose.sobriety.Addiction;
at com.katiearose.sobriety.CacheHandler.readCache(CacheHandler.kt:17)
at com.katiearose.sobriety.Main.onCreate(Main.kt:61)
I don't really get why it crashes, because there is try catch blocks everywhere that should have caught that exception.
Mabye its worth considering to use a different way of storage, since these FileOutPutStreams etc. are a bit tricky to debug. Since the data is very simple, you could use Android's Shared Preferences, and save the list as a serialized string in a single key value pair there. Or if you want to debug the current system, here is the full crash log:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.katiearose.sobriety, PID: 21703
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.katiearose.sobriety/com.katiearose.sobriety.Main}: java.io.InvalidClassException: com.katiearose.sobriety.Addiction; local class incompatible: stream classdesc serialVersionUID = -8477717136644775154, local class serialVersionUID = 7971970991809636397
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3635)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3792)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2210)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7839)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Caused by: java.io.InvalidClassException: com.katiearose.sobriety.Addiction; local class incompatible: stream classdesc serialVersionUID = -8477717136644775154, local class serialVersionUID = 7971970991809636397
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:624)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1713)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1594)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1872)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1412)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:427)
at java.util.ArrayList.readObject(ArrayList.java:791)
at java.lang.reflect.Method.invoke(Native Method)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1066)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2013)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1899)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1412)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:427)
at com.katiearose.sobriety.CacheHandler.readCache(CacheHandler.kt:17)
at com.katiearose.sobriety.Main.onCreate(Main.kt:61)
at android.app.Activity.performCreate(Activity.java:8051)
at android.app.Activity.performCreate(Activity.java:8031)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1329)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3608)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3792)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2210)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7839)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Many people stop for a length of time rather then a one time relapse. Then they restart abstaining from whatever. It would be nice if that was an option over just a relapse button that restarts automatically.
Maybe keep time since abstaining was stopped.
I personally would feel better if i could restart a fresh new abstaining attempt from an existing button if i have stopped for a few days, where i would likely trash the last attempt over use the relapse button when i wanted to try abstaining again
While it's nice to have days hours, etc, not so much after you have over 1000 days.
It's much more readable if there was an option for years, months, days, hours seconds.
Personally i don't even need exact months since one is 28 days and others are 30 or 31, but it would be nice for the crazy exact people.
It would be great to be able to release Sobriety on iOS but a lot of work would need to be done on making the code work on both platforms. Luckily Kotlin can be used for both, but Android APIs wouldn't work and neither would many Google libraries I would assume. This is a very long term goal.
Hi, I recently started using your app to stop my addiction. Thank you for developing the app.
I found out that sometimes the time since last relapse is one day more than the real value.
How to reproduce the bug:
Version v10.0.0
Now that we have a flexible deserializer, I was thinking to refactor the Addiction class. Rather than needing to use isStopped and isFuture(), what do you think about a state system using an enum? Also, lastRelapse and timeStopped are both already found in the history, so maybe they can be converted to functions. The exception to this is that lastRelapse stores future start times before they are added into history, but this can be changed. Lastly, I think we can remove the relapses CircularBuffer since we calculate averages in a different way now.
To keep these changes consistent with past versions, the deserializer will:
timeStopped
and relapses
lastRelapse
to calculate if isFuture() and otherwise discard the valueisStopped
to translate into the status enum.Summary:
Remove lastRelapse
, isStopped
, timeStopped
, relapses
Add status
enum {Ongoing, Stopped, Future}
Let me know if this is reasonable.
Maybe just save each note with its timestamp instead of just the date? This way every relapse can have a note. I'll get started on this but if someone beats me to it that's great too.
Currently, the relapses CircularBuffer is not updated and the average relapse duration is not shown
There seems to be an issue when updating the app, and then reading a save file created in the old version.
Steps to reproduce:
1 Checkout version 3.0.0
2 Add a couple of entries
3 Checkout the newest version => update the app => start the app => app crashes with this error:
java.lang.OutOfMemoryError: OutOfMemoryError thrown while trying to throw OutOfMemoryError; no stack trace available
Re-installing the app fixes this bug but also gets rid of previous entries.
1 Theme inconsistency: Currently the dark theme does have a title bar showing the app name, the light theme does not have one.
2 Improvement idea: Don't show text parts containing 0's.
E.g. for a new entry, don't show 0 days 0 hours etc, initially just show the seconds.
3 Bug or feature? Its currently possible to set a date / time in the future, which then results in a negative time counting up.
It would be nice to have an About section in the settings that includes this info:
I'm sure there is other information that I can't think of right now.
Ulefone armor 7
Installed fresh version from F-DROID today.
It seems that when I set a new addiction and mark a start time yesterday afternoon, it adds a day to the calculation.
It was 09:00 (9am) today (6th) and I set an addiction start to 23:20 (11:23pm) last night (5th). It showed me 1 day and 9 hours, as it would elapse another day since that.
Since the recent average is calculated from the last 3 relapses, it is inaccurate when there are less than 3 relapses in the timeline.
To reproduce:
Let's say for example i forgot to reset my timer 3 days ago and want to do it now. How do i set the new start date to this specific date? Pressing the relapse button just resets it to the current time.
Right now the only way to include new translations is by manually translating the strings.xml
which is... not the best for regular users (error-prone, no way to track progress, etc.). I propose implementing integration with a service (I recommend Weblate!) to ease the task, like, a lot for casual translators.
@IzzySoft it seems like the images from Fastlane are not being updated on F-Droid and I'm not sure why. Any ideas?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.