├── .gitignore ├── LICENSE ├── README.md ├── api ├── __init__.py ├── admin.py ├── migrations │ └── __init__.py ├── models.py ├── tests.py ├── urls.py └── views.py ├── iHealth_site ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py ├── log └── .gitkeep ├── manage.py ├── uwsgi.ini └── uwsgiserver.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.log 3 | .idea 4 | db.sqlite3 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## iHealth-site 2 | iHealth 项目的后台程序 3 | 4 | ### 依赖 5 | * Django==1.8.17 6 | * pymongo==3.4.0 7 | * uWSGI==2.0.15 8 | 9 | ### 启动项目 10 | 1. 本地启动(windows): 11 | ``` 12 | python manage.py runserver 0.0.0.0:8000 13 | ``` 14 | 15 | 2. 打开浏览器访问 **http://127.0.0.1:8000/** 16 | 出现 **Hello, I am iHealth ' backend!** 表示启动成功 17 | 18 | 3. 测试其它接口 19 | 获取文章列表:http://127.0.0.1:8000/api/v1/articlelist 20 | 获取文章详情:http://127.0.0.1:8000/api/v1/articledetail?id=59eefad4e6c80c707840adc2 21 | 22 | ### TODO 23 | * [x] 【接口】获取文章首页列表 24 | * [x] 【接口】获取文章详情 25 | * [x] 【接口】用户登陆验证 26 | * [x] 【接口】模糊匹配指定用户名,返回符合的用户列表 27 | * [x] 【接口】获取用户详情 28 | * [x] 【接口】用户注册 29 | * [x] 【接口】根据 id 批量请求用户信息 30 | * [x] 【接口】点赞数更新 31 | * [x] 【接口】推荐列表 个性化推荐文章 32 | * [ ] 【接口】获取用户个人病历 33 | * [ ] 【接口】获取某用户的主治医生信息 34 | * [x] 【接口】个人信息的修改 35 | * [x] 【接口】医生为病人添加病历 36 | 37 | ### 接口说明 38 | API 接口采用 RESTful 规范设计 39 | 40 | 什么是 RESTful 请看:[怎样用通俗的语言解释REST,以及RESTful? - 知乎](https://www.zhihu.com/question/28557115) 41 | 42 | 43 | ---- 44 | * 获取首页文章列表 45 | 46 | 示例:http://ihealth.yangyingming.com/api/v1/articlelist?page=1&limit=10&userID=5a02e30be6c80c1c9ecdaea7&cate=recommend 47 | 48 | 请求方式:GET 49 | 50 | | 参数 | 默认值 | 说明 | 51 | | -------- | -------- | -------- | 52 | | page | 1 | 取第几页的数据 | 53 | | limit | 10 | 一次取多少个 | 54 | | userID | None | 用户ID,个性化推荐用 | 55 | | cate | None | 首页文章分类,'recommend'为推荐 | 56 | 57 | ---- 58 | 59 | * 获取文章详情 60 | 61 | 示例:http://ihealth.yangyingming.com/api/v1/articledetail?id=59eefad4e6c80c707840adc2&userID=5a02e30be6c80c1c9ecdaea7 62 | 63 | 请求方式:GET 64 | 65 | | 参数 | 默认值 | 说明 | 66 | | -------- | -------- | -------- | 67 | | id | None | 取指定id的文章详情 | 68 | | userID | None | 用户ID,个性化推荐用 | 69 | 70 | ---- 71 | 72 | * 点赞接口 73 | 74 | 示例:http://ihealth.yangyingming.com/api/v1/updateUpvote?id=59eefad4e6c80c707840adc2&userID=5a02e30be6c80c1c9ecdaea7 75 | 76 | 请求方式:GET 77 | 78 | | 参数 | 默认值 | 说明 | 79 | | -------- | -------- | -------- | 80 | | id | None | 取指定id的文章详情 | 81 | | userID | None | 用户ID,个性化推荐用 | 82 | 83 | ---- 84 | 85 | * 用户登陆验证 86 | 87 | 示例:http://ihealth.yangyingming.com/api/v1/usercheck 88 | 89 | 请求方式:POST 90 | 91 | | 参数 | 默认值 | 说明 | 92 | | -------- | -------- | -------- | 93 | | email | None | 待验证用户的邮箱 | 94 | | password | None | 待验证用户的密码 | 95 | 96 | ---- 97 | * 模糊匹配指定用户名,返回符合的用户列表 98 | 99 | 示例:http://ihealth.yangyingming.com/api/v1/userlist?name=小明 100 | 101 | 请求方式:GET 102 | 103 | | 参数 | 默认值 | 说明 | 104 | | -------- | -------- | -------- | 105 | | name | '' | 模糊匹配条件 | 106 | | selfname | '' | 需要排除的name值 | 107 | | limit | 10 | 最多匹配多少条结果 | 108 | 109 | ---- 110 | 111 | * 获取用户详情 112 | 113 | 示例:http://ihealth.yangyingming.com/api/v1/user?id=59fb1595dfdeee2b4c26c346 114 | 115 | 请求方式:GET 116 | 117 | | 参数 | 默认值 | 说明 | 118 | | -------- | -------- | -------- | 119 | | id | None | 用户id | 120 | 121 | ---- 122 | 123 | * 用户注册 124 | 125 | 示例:http://ihealth.yangyingming.com/api/v1/reguser 126 | 127 | 请求方式:POST 128 | 129 | | 参数 | 默认值 | 说明 | 130 | | -------- | -------- | -------- | 131 | | email | None | 注册邮箱 | 132 | | password | None | 密码 | 133 | | nickname | None | 用户昵称 | 134 | | name | None |用户真实姓名 | 135 | | sex | None | 性别 0:女 1:男| 136 | | usertype | None | 用户类别 0:游客 1:患者 2:医生 3:管理员 | 137 | | birthday | None | 用户出生日期 | 138 | | introduction | None | 个人介绍 | 139 | | age | None | 用户年龄 | 140 | | phone | None | 注册手机号 | 141 | 142 | ---- 143 | 144 | * 根据ID批量获取用户信息 145 | 146 | 示例:http://ihealth.yangyingming.com/api/v1/userlistbyid?id=59fb1595dfdeee2b4c26c347,59fb1595dfdeee2b4c26c348 147 | 148 | 请求方式:GET 149 | 150 | | 参数 | 默认值 | 说明 | 151 | | -------- | -------- | -------- | 152 | | id | None | 要获取的用户id,可以是多个 | 153 | 154 | ---- 155 | 156 | * 修改昵称 157 | 158 | 示例:http://ihealth.yangyingming.com/api/v1/changeNickname 159 | 160 | 请求方式:POST 161 | 162 | | 参数 | 默认值 | 说明 | 163 | | -------- | -------- | -------- | 164 | | id | None | 要修改昵称的用户id | 165 | | newName | None | 修改后的昵称 | 166 | 167 | ---- 168 | 169 | * 修改手机号 170 | 171 | 示例:http://ihealth.yangyingming.com/api/v1/changePhone 172 | 173 | 请求方式:POST 174 | 175 | | 参数 | 默认值 | 说明 | 176 | | -------- | -------- | -------- | 177 | | id | None | 要修改手机号的用户id | 178 | | newPhone | None | 修改后的手机号 | 179 | 180 | ---- 181 | 182 | * 修改姓名 183 | 184 | 示例:http://ihealth.yangyingming.com/api/v1/changeName 185 | 186 | 请求方式:POST 187 | 188 | | 参数 | 默认值 | 说明 | 189 | | -------- | -------- | -------- | 190 | | id | None | 要修改姓名的用户id | 191 | | newName | None | 修改后的姓名 | 192 | 193 | ---- 194 | 195 | * 修改性别 196 | 197 | 示例:http://ihealth.yangyingming.com/api/v1/changeSex 198 | 199 | 请求方式:POST 200 | 201 | | 参数 | 默认值 | 说明 | 202 | | -------- | -------- | -------- | 203 | | id | None | 要修改性别的用户id | 204 | | newSex | None | 修改后的性别 | 205 | 206 | ---- 207 | 208 | * 修改密码 209 | 210 | 示例:http://ihealth.yangyingming.com/api/v1/changePassword 211 | 212 | 请求方式:POST 213 | 214 | | 参数 | 默认值 | 说明 | 215 | | -------- | -------- | -------- | 216 | | id | None | 要修改密码的用户id | 217 | | oldPassword | None | 原密码 | 218 | | newPassword | None | 新密码 | 219 | 220 | ---- 221 | 222 | * 修改出生日期 223 | 224 | 示例:http://ihealth.yangyingming.com/api/v1/changeBirthday 225 | 226 | 请求方式:POST 227 | 228 | | 参数 | 默认值 | 说明 | 229 | | -------- | -------- | -------- | 230 | | id | None | 要修改出生日期的用户id | 231 | | newBirthday | None | 新的出生日期 | 232 | 233 | ---- 234 | 235 | * 添加病历 236 | 237 | 示例:http://ihealth.yangyingming.com/api/v1/addMedicalRecord 238 | 239 | 请求方式:POST 240 | 241 | | 参数 | 默认值 | 说明 | 242 | | -------- | -------- | -------- | 243 | | id | None | 要添加病历的用户id | 244 | | date | None | 添加病历的日期 | 245 | | doctor | None | 添加病历的主治医生 | 246 | | content | None | 病历内容 | 247 | 248 | ---- 249 | 250 | ### Django 搭建笔记(笔记部分,和项目无关) 251 | 1. 创建项目目录 252 | ``` 253 | django-admin startproject iHealth_site 254 | ``` 255 | 256 | 2. 运行自带服务器进行测试 257 | ``` 258 | python manage.py runserver 0.0.0.0:8000 259 | ``` 260 | **注意**:想要外网访问需要在 settings.py 的 ALLOWED_HOSTS = ['\*'] 261 | 262 | 3. 创建app目录 263 | ``` 264 | python manage.py startapp mysite 265 | ``` 266 | 267 | ### MongoDB 配置 268 | 1. 开启 MongoDB 权限认证:**在配置文件中加入 auth = true** 269 | 270 | 2. 创建管理员用户(如果你是第一次使用 MongoDB) 271 | ``` 272 | use admin 273 | db.createUser({user:"admin",pwd:"admin123",roles:["userAdminAnyDatabase"]}) 274 | ``` 275 | 管理员用户用来创建其他数据库和用户 276 | 277 | 3. 使用管理员账户远程登录 278 | ``` 279 | C:\Users\cs>mongo [your_ip]:27017 280 | > use admin 281 | switched to db admin 282 | > db.auth('admin','admin123') 283 | 1 284 | ``` 285 | 286 | 4. 创建 iHealth 数据库,以及操作该数据库的用户 287 | ``` 288 | use iHealth // 创建 iHealth 数据库,并作为认证数据库 289 | db.createUser({ 290 | user:'admin', // 用户名 291 | pwd:'admin123', // 用户密码 292 | roles:[{role:'readWrite',db:'iHealth'}] // 为该用户赋予数据库的读写权限 293 | }) 294 | ``` 295 | 296 | 5. 使用该用户远程登录 iHealth 数据库 297 | ``` 298 | C:\Users\cs>mongo [your_ip]:27017 299 | > use iHealth 300 | switched to db iHealth 301 | > db.auth('admin','admin123') 302 | 1 303 | > db.getCollectionNames() 304 | [ ] 305 | ``` 306 | 数据库刚刚创建,所以没有数据 307 | 308 | ### 参考资料 309 | * client 提交post 到 django出现403错误 310 | http://blog.csdn.net/watsy/article/details/9009847 311 | -------------------------------------------------------------------------------- /api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iHealth-ecnu/iHealth_site/59cbdac8b574e8c97b47896ebb8a56f22e0af33e/api/__init__.py -------------------------------------------------------------------------------- /api/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /api/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iHealth-ecnu/iHealth_site/59cbdac8b574e8c97b47896ebb8a56f22e0af33e/api/migrations/__init__.py -------------------------------------------------------------------------------- /api/models.py: -------------------------------------------------------------------------------- 1 | #coding=utf8 2 | from django.db import models 3 | from django.conf import settings 4 | from bson.objectid import ObjectId 5 | import pymongo 6 | 7 | class Articles(): 8 | def __init__(self): 9 | '''初始化''' 10 | # 连接 mongo 数据库,获得数据指定集合 11 | self.client = pymongo.MongoClient('mongodb://%s:%s@%s:%d/%s'%(settings. 12 | MONGO_USER,settings.MONGO_PWD,settings.MONGO_HOST,settings.MONGO_PORT,settings. 13 | MONGO_AUTHDB))[settings.MONGO_DBNAME] 14 | self.articles = self.client['articles'] 15 | 16 | def find_one(self,id): 17 | '''获取指定数据''' 18 | article = self.articles.find_one({"_id": ObjectId(id)}) 19 | return article 20 | 21 | def find_all(self): 22 | '''返回全部文章''' 23 | '''默认按照倒序排列,即取出最新插入的文章''' 24 | article_list = self.articles.find().sort('_id', pymongo.DESCENDING) 25 | return article_list 26 | 27 | def updateRead(self,id=None,cnt=1): 28 | '''阅读量+1''' 29 | if id == None: 30 | raise Exception,'请提供 id 参数!' 31 | self.articles.update_one({'_id':ObjectId(id)},{'$inc':{'read':cnt}}) 32 | 33 | def find_recommendArticle(self, labels): 34 | '''根据用户的兴趣label选择文章序列并返回''' 35 | #labels按照值降序排列,放在label_list中 36 | label_list = sorted(labels.items(), key=lambda e:e[1], reverse=True) 37 | #只取前5个兴趣label 38 | if len(label_list) > 5: 39 | label_list = label_list[0:5] 40 | #返回用户感兴趣的所有文章 41 | #article_C 为放置各个分类文章的容器 42 | article_C = [] 43 | for category in label_list: 44 | article_C.append((self.articles.find({'category':category[0] }).sort('_id', pymongo.DESCENDING))) 45 | return article_C 46 | 47 | def find_labelArticle(self, label): 48 | '''返回单个label的文章列表''' 49 | article_list = self.articles.find({'category': label}).sort('_id', pymongo.DESCENDING) 50 | return article_list 51 | 52 | def updateUpvote(self,id=None): 53 | '''点赞接口''' 54 | if id == None: 55 | raise Exception,'请提供 id 参数!' 56 | self.articles.update_one({'_id':ObjectId(id)},{'$inc':{'upvote':1}}) 57 | 58 | 59 | class Users(): 60 | def __init__(self): 61 | '''初始化''' 62 | # 连接 mongo 数据库,获得数据指定集合 63 | self.client = pymongo.MongoClient('mongodb://%s:%s@%s:%d/%s'%(settings. 64 | MONGO_USER,settings.MONGO_PWD,settings.MONGO_HOST,settings.MONGO_PORT,settings. 65 | MONGO_AUTHDB))[settings.MONGO_DBNAME] 66 | self.users = self.client['users'] 67 | 68 | def find_one(self,id): 69 | '''获取指定数据''' 70 | user = self.users.find_one({"_id": ObjectId(id)}) 71 | return user 72 | 73 | def find_label(self, id): 74 | '''获取用户对应的labels''' 75 | user = self.users.find_one({"_id": ObjectId(id)}) 76 | if user.has_key('labels'): 77 | return user['labels'] 78 | else: #用户没有label 79 | return None 80 | 81 | def find_all(self): 82 | '''返回全部用户数据''' 83 | user_list = self.users.find() 84 | return user_list 85 | 86 | def find_one_by_email(self,email): 87 | '''获取指定数据''' 88 | user = self.users.find_one({"email": email}) 89 | return user 90 | 91 | def find_many_by_name(self,name): 92 | '''获取指定数据''' 93 | # 正则方式模糊查询 + 类全文索引 94 | user_list = self.users.find({ '$or' : [{ 'name' : { '$regex' : name } }, { 'nickname' : { '$regex' : name } }] }) 95 | return user_list 96 | 97 | def insert_one(self,data): 98 | '''插入数据''' 99 | self.users.insert_one(data) 100 | 101 | def insert_label(self, id): 102 | '''给没有labels的用户设置空labels''' 103 | if self.find_label(id) == None: 104 | self.users.update_one({"_id": ObjectId(id)},{'$set':{'labels':{}}}) 105 | 106 | def update_label(self, id, label, value): 107 | '''更新labels中label的值''' 108 | #没有labels的用户首先设置label 109 | self.insert_label(id) 110 | #首先找到对应用户的labels 111 | user = self.users.find_one({"_id": ObjectId(id)}) 112 | cur_labels = user['labels'] 113 | if cur_labels.has_key(label): 114 | cur_labels[label] = cur_labels[label] + value 115 | else: #新增对应label 116 | cur_labels[label] = value 117 | self.users.update_one({"_id": ObjectId(id)}, {'$set': {'labels':cur_labels}}) 118 | 119 | #修改昵称 120 | def changeNickname(self,id=None,newName=None): 121 | if id == None or newName == None: 122 | raise Exception,'请提供 id 和昵称完整参数!' 123 | self.users.update_one({'_id':ObjectId(id)},{'$set':{'nickname':newName}}) 124 | 125 | #修改手机号 126 | def changePhone(self,id=None,newPhone=None): 127 | if id == None or newPhone == None: 128 | raise Exception,'请提供 id 和手机号完整参数!' 129 | self.users.update_one({'_id':ObjectId(id)},{'$set':{'phone':newPhone}}) 130 | 131 | #修改用户姓名 132 | def changeName(self,id=None,newName=None): 133 | if id == None or newName == None: 134 | raise Exception,'请提供 id 和姓名完整参数!' 135 | self.users.update_one({'_id':ObjectId(id)},{'$set':{'name':newName}}) 136 | 137 | #修改用户性别 138 | def changeSex(self,id=None,newSex=None): 139 | if id == None or newSex == None: 140 | raise Exception,'请提供 id 和 性别 完整参数!' 141 | self.users.update_one({'_id':ObjectId(id)},{'$set':{'sex':int(newSex)}}) 142 | 143 | #根据id返回加密后的密码,用于在修改时的密码认证 144 | def get_password_by_id(self,id=None): 145 | if id == None: 146 | raise Exception,'请提供 id 完整参数!' 147 | user = self.users.find_one({"_id": ObjectId(id)}) 148 | return user['password'] 149 | 150 | #修改密码 151 | def changePassword(self,id=None,newPwd=None): 152 | if id == None or newPwd == None: 153 | raise Exception,'请提供 id 和 密码 完整参数!' 154 | self.users.update_one({'_id':ObjectId(id)},{'$set':{'password':newPwd}}) 155 | 156 | #修改出生日期 157 | def changeBirthday(self,id=None,newBirthday=None): 158 | if id == None or newBirthday == None: 159 | raise Exception,'请提供 id 和 日期 完整参数!' 160 | self.users.update_one({'_id':ObjectId(id)},{'$set':{'birthday':newBirthday}}) 161 | 162 | #添加病历 163 | def addMedicalRecord(self,id=None,data=None): 164 | if id == None or data == None: 165 | raise Exception,'请提供 id 和 病历 完整参数!' 166 | user = self.users.find_one({"_id":ObjectId(id)}) 167 | temp = list(user['medicalRecord'] or ['']) 168 | temp = temp.append(data) 169 | self.users.update_one({'_id':ObjectId(id)},{'$set':{'medicalRecord':temp}}) 170 | -------------------------------------------------------------------------------- /api/tests.py: -------------------------------------------------------------------------------- 1 | # 无可奉告 一颗赛艇 2 | # uJjYJYYLLv7r7vJJ5kqSFFFUUjJ7rrr7LLYLJLJ7 3 | # JuJujuYLrvuEM@@@B@@@B@B@B@@@MG5Y7vLjYjJL 4 | # JYjYJvr7XM@BB8GOOE8ZEEO8GqM8OBBBMu77LLJ7 5 | # LJLY7ru@@@BOZ8O8NXFFuSkSu25X0OFZ8MZJ;vLv 6 | # YvL7i5@BM8OGGqk22uvriiriii;r7LuSZXEMXrvr 7 | # vv7iU@BMNkF1uY7v7rr;iiii:i:i:ii7JEPNBPir 8 | # L7iL@BM8Xjuujvv77rr;ri;i;:iiiii:iLXFOBJ: 9 | # 7ri@B@MOFuUS2Y7L7777rii;:::::i:iirjPG@O: 10 | # 7:1B@BBOPjXXSJvrL7rr7iiii:i::::i;iv5MBB, 11 | # r:0@BBM8SFPX2Y77rri::iirri:::::iii75O@G. 12 | # 7:SB@BBGqXPk0122UJL::i::r:::i:i;i:v2@Bk. 13 | # ri:MB@BBEqEMGq2JLLL1u7.iX51u77LF27iSB@r, 14 | # ri,v@B@MB8@qqNEqN1u:5B8BOFE0S7ii7qMB@F:: 15 | # ii,J80Eq1MZkqPPX5YkPE@B@iXPE52j7:vBjE7:: 16 | # ii:7MSqkS0PvLv7rrii0@L.Z1iLr::ir:rO,vi:: 17 | # ii::EZXPSkquLvii:iF@N:.,BUi7ri,::UY;r::: 18 | # i::.2ONXqkPXS5FUUEOPP;..iSPXkjLYLLrr:::, 19 | # :::,iMXNP0NPLriiLGZ@BB1P87;JuL7r:7ri:::, 20 | # :::,.UGqNX0EZF2uUjUuULr:::,:7uuvv77::::. 21 | # ::::..5OXqXNJ50NSY;i:.,,,:i77Yvr;v;,,::. 22 | # :::,:.jOEPqPJiqBMMMO8NqP0SYLJriirv:.:,:. 23 | # ,:,,,.,Zq0P0X7vPFqF1ujLv7r:irrr7j7.,,::. 24 | # ,,,....0qk0080v75ujLLv7ri:i:rvj2J...,,,. 25 | # ......8@UXqZEMNvJjr;ii::,:::7uuv...,.,,. 26 | # .....B@BOvX88GMGk52vririiirJS1i.......,. 27 | # .JEMB@B@BMvL0MOMMMO8PE8GPqSk2L:......... 28 | # @B@@@B@M@B@L:7PGBOO8MOMOEP0Xri@B@Mk7,... 29 | # B@B@BBMBB@B@0::rJP8MO0uvvu7..,B@B@B@B@Z7 30 | # MMBM@BBB@B@B@Br:i,..:Lur:....7@OMMBM@B@@ 31 | # 8OOMMMOMMMMBB@B:....,PZENNi..JBOZ8GMOOOO 32 | 33 | 34 | from django.test import TestCase 35 | from django.test import Client 36 | 37 | import random 38 | import json 39 | 40 | 41 | from .views import MD5 42 | class EncryptTestCase(TestCase): 43 | def testMD5(self): 44 | self.assertEqual(MD5(''), 'd41d8cd98f00b204e9800998ecf8427e') 45 | self.assertEqual(MD5('a'), '0cc175b9c0f1b6a831c399e269772661') 46 | self.assertEqual(MD5('abc'), '900150983cd24fb0d6963f7d28e17f72') 47 | self.assertEqual(MD5('abcdefghijklmnopqrstuvwxyz'), 48 | 'c3fcd3d76192e4007dfb496cca67e13b') 49 | self.assertEqual(MD5('123456789012345678901234567890123456789012'\ 50 | '34567890123456789012345678901234567890'), 51 | '57edf4a22be3c955ac49da2e2107b67a') 52 | self.assertEqual(MD5('message digest'), 'f96b697d7cb7938d525a2f31aaf161d0') 53 | 54 | 55 | from .views import articleList 56 | class ViewTestCase(TestCase): 57 | setup_done = False 58 | 59 | def test_return_code(self): 60 | response = self.client.get('/api/v1/hello/') 61 | self.assertEqual(response.status_code, 200) 62 | 63 | response = self.client.get('/api/v1/articlelist', \ 64 | {'page': '1', 'limit': '10'}) 65 | self.assertEqual(response.status_code, 200) 66 | 67 | response = self.client.get('/api/v1/articledetail', \ 68 | {'id':'5a02e30be6c80c1c9ecdaea7'}) 69 | self.assertEqual(response.status_code, 200) 70 | 71 | response = self.client.get('/api/v1/updateUpvote', \ 72 | {'id':'', 'userID':''}) 73 | self.assertEqual(response.status_code, 200) 74 | 75 | response = self.client.post('/api/v1/usercheck', \ 76 | {'email':'', 'password':''}) 77 | self.assertEqual(response.status_code, 200) 78 | 79 | response = self.client.get('/api/v1/userlist', \ 80 | {'name': '', 'selfname': '', 'limit': ''}) 81 | self.assertEqual(response.status_code, 200) 82 | 83 | response = self.client.get('/api/v1/user', \ 84 | {'id': ''}) 85 | self.assertEqual(response.status_code, 200) 86 | 87 | response = self.client.post('/api/v1/reguser', \ 88 | {'email': '', 'password': '', 'nickname': '', \ 89 | 'name': '', 'sex': '', 'usertype': '', \ 90 | 'birthday': '', 'introduction': '', 'age':'', 91 | 'phone':''}) 92 | self.assertEqual(response.status_code, 200) 93 | 94 | response = self.client.get('/api/v1/userlistbyid', \ 95 | {'id': ''}) 96 | self.assertEqual(response.status_code, 200) 97 | 98 | response = self.client.post('/api/v1/changeNickname', \ 99 | {'id': '', 'newName': ''}) 100 | self.assertEqual(response.status_code, 200) 101 | 102 | response = self.client.post('/api/v1/userlistbyid', \ 103 | {'id': ''}) 104 | self.assertEqual(response.status_code, 200) 105 | 106 | response = self.client.post('/api/v1/changePhone', \ 107 | {'id': '', 'newPhone':''}) 108 | self.assertEqual(response.status_code, 200) 109 | 110 | response = self.client.post('/api/v1/changeName', \ 111 | {'id': '', 'newName': ''}) 112 | self.assertEqual(response.status_code, 200) 113 | 114 | response = self.client.post('/api/v1/changeSex', \ 115 | {'id': '', 'newSex':''}) 116 | self.assertEqual(response.status_code, 200) 117 | 118 | response = self.client.post('/api/v1/changePassword', \ 119 | {'id': '', 'oldPassword':'', 'newPassword':''}) 120 | self.assertEqual(response.status_code, 200) 121 | 122 | response = self.client.post('/api/v1/changeBirthday', \ 123 | {'id': '', 'newBirthday':''}) 124 | self.assertEqual(response.status_code, 200) 125 | 126 | response = self.client.post('/api/v1/addMedicalRecord', \ 127 | {'id': '', 'date':'', 'doctor': '', 'content':''}) 128 | self.assertEqual(response.status_code, 200) 129 | 130 | def setUp(self): 131 | ''' 132 | first testing user reg for rest tests; 133 | ''' 134 | if self.setup_done: 135 | return 136 | self.setup_done = True 137 | 138 | response = self.client.post('/api/v1/reguser', \ 139 | {'email': 'admin@mypre.cn', 'password': MD5('123456'), 'nickname': 'lanbing', \ 140 | 'name': 'lanbing_h', 'sex': '1', 'usertype': '1', \ 141 | 'birthday': '1999-11-20', 'introduction': 'new lanbing huangzhe', 'age':'11', 142 | 'phone':'123'}) 143 | self.assertEqual(response.status_code, 200) 144 | 145 | # register repeat user test 146 | response = self.client.post('/api/v1/reguser', \ 147 | {'email': 'admin@mypre.cn', 'password': MD5('123456'), 'nickname': 'lanbing', \ 148 | 'name': 'lanbing_h', 'sex': '1', 'usertype': '1', \ 149 | 'birthday': '1999-11-20', 'introduction': 'new lanbing huangzhe', 'age':'11', 150 | 'phone':'123'}) 151 | self.assertEqual(response.status_code, 200) 152 | self.assertIn('false',response.content) 153 | 154 | def test_user_passwd(self): 155 | # user passwd test 156 | response = self.client.post('/api/v1/usercheck', \ 157 | {'email':'admin@mypre.cn', 'password':MD5('123456')}) 158 | self.assertEqual(response.status_code, 200) 159 | self.assertIn('true',response.content) 160 | 161 | json_data = json.loads(response.content) 162 | 163 | self.user_id = json_data['data']['_id'] 164 | 165 | def test_user_detail(self): 166 | self.test_user_passwd() 167 | response = self.client.get('/api/v1/user', \ 168 | {'id': self.user_id}) 169 | self.assertEqual(response.status_code, 200) 170 | json_data = json.loads(response.content) 171 | self.assertIn('name',response.content) 172 | self.assertIn('phone', response.content) 173 | self.assertIn('labels', response.content) 174 | self.assertIn('_id', json_data) 175 | 176 | def test_article_list(self): 177 | for pagei in xrange(1, 5): 178 | response = self.client.get('/api/v1/articlelist', \ 179 | {'page': pagei, 'limit': '10'}) 180 | self.assertEqual(response.status_code, 200) 181 | json_data = json.loads(response.content) 182 | self.assertEqual(len(json_data), 10) 183 | self.article_list = json_data 184 | 185 | def test_article_detail(self): 186 | self.test_user_passwd() 187 | self.test_article_list() 188 | self.test_user_passwd() 189 | response = self.client.get('/api/v1/articledetail', \ 190 | {'id': self.article_list[0]['_id'], 'userID': self.user_id}) 191 | self.assertEqual(response.status_code, 200) 192 | # self.article_detail = json.loads() 193 | self.assertIn('href', response.content) 194 | 195 | def test_article_upvote(self): 196 | self.test_user_passwd() 197 | self.test_article_list() 198 | response = self.client.get('/api/v1/updateUpvote', \ 199 | {'id': self.article_list[0]['_id'], 'userID': self.user_id}) 200 | self.assertEqual(response.status_code, 200) 201 | 202 | def test_userlist(self): 203 | response = self.client.get('/api/v1/userlist', \ 204 | {'name': 'l', 'selfname': '', 'limit': ''}) 205 | self.assertEqual(response.status_code, 200) 206 | # self.assertIn('_id', response.content) 207 | 208 | response = self.client.get('/api/v1/userlist', \ 209 | {'name': 'asdfsdf', 'selfname': '', 'limit': '30'}) 210 | self.assertEqual(response.status_code, 200) 211 | self.assertNotIn('_id', response.content) 212 | 213 | def test_change_phone(self): 214 | self.test_user_passwd() 215 | response = self.client.post('/api/v1/changePhone', \ 216 | {'id': self.user_id, 'newPhone':'456', 'password':MD5('123456')}) 217 | self.assertEqual(response.status_code, 200) 218 | self.assertIn('true', response.content) 219 | 220 | response = self.client.post('/api/v1/changePhone', \ 221 | {'id': self.user_id, 'newPhone':'456', 'password':MD5('!!!')}) 222 | self.assertEqual(response.status_code, 200) 223 | self.assertIn('false', response.content) 224 | 225 | def test_change_name(self): 226 | self.test_user_passwd() 227 | response = self.client.post('/api/v1/changeName', \ 228 | {'id': self.user_id, 'newName': 'lanbing new Name', 'password':MD5('123456')}) 229 | self.assertEqual(response.status_code, 200) 230 | self.assertIn('true', response.content) 231 | 232 | response = self.client.post('/api/v1/changeName', \ 233 | {'id': self.user_id, 'newName': 'lanbing new Name', 'password':'123'}) 234 | self.assertEqual(response.status_code, 200) 235 | self.assertIn('false', response.content) 236 | 237 | def test_change_sex(self): 238 | self.test_user_passwd() 239 | response = self.client.post('/api/v1/changeSex', \ 240 | {'id': self.user_id, 'newSex':'1', 'password':MD5('123456')}) 241 | self.assertEqual(response.status_code, 200) 242 | self.assertIn('true', response.content) 243 | 244 | response = self.client.post('/api/v1/changeSex', \ 245 | {'id': self.user_id, 'newSex':'1', 'password':'sdf'}) 246 | self.assertEqual(response.status_code, 200) 247 | self.assertIn('false', response.content) 248 | 249 | response = self.client.post('/api/v1/changeSex', \ 250 | {'id': self.user_id, 'newSex':'1'}) 251 | self.assertEqual(response.status_code, 200) 252 | self.assertIn('false', response.content) 253 | 254 | def test_change_passwd(self): 255 | self.test_user_passwd() 256 | response = self.client.post('/api/v1/changePassword', \ 257 | {'id': self.user_id, 'oldPassword':MD5('123456'), 'newPassword':MD5('123456')}) 258 | self.assertEqual(response.status_code, 200) 259 | self.assertIn('true', response.content) 260 | 261 | def test_change_birthday(self): 262 | self.test_user_passwd() 263 | response = self.client.post('/api/v1/changeBirthday', \ 264 | {'id': self.user_id, 'newBirthday': '1999-10-10', 'password':MD5('123456')}) 265 | self.assertEqual(response.status_code, 200) 266 | self.assertIn('true', response.content) 267 | 268 | def test_change_nickname(self): 269 | self.test_user_passwd() 270 | response = self.client.post('/api/v1/changeNickname', \ 271 | {'id': self.user_id, 'newName': 'lanbing\' new nickname!!!', 'password':MD5('123456')}) 272 | self.assertEqual(response.status_code, 200) 273 | self.assertIn('true', response.content) 274 | 275 | def test_add_medical_record(self): 276 | self.test_user_passwd() 277 | response = self.client.post('/api/v1/addMedicalRecord', \ 278 | {'id': self.user_id, 'date':'21312423', \ 279 | 'doctor': 'JiangYiShen', 'content':'I\'m not sick!!!', \ 280 | 'password':MD5('123456')}) 281 | self.assertEqual(response.status_code, 200) 282 | 283 | self.assertIn('true', response.content) 284 | 285 | 286 | -------------------------------------------------------------------------------- /api/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | url(r'^hello/', views.hello), 7 | url(r'^articlelist', views.articleList), 8 | url(r'^article', views.articleDetail), 9 | url(r'^usercheck', views.userCheck), 10 | url(r'^userlist$', views.userList), 11 | url(r'^user$', views.userDetail), 12 | url(r'^reguser', views.regUser), 13 | url(r'^userlistbyid$',views.userListByID), 14 | url(r'^updateUpvote$',views.doUpvote), 15 | url(r'^changeNickname$',views.changeNickname), 16 | url(r'^changePhone$',views.changePhone), 17 | url(r'^changeName$',views.changeName), 18 | url(r'^changeSex$',views.changeSex), 19 | url(r'^changePassword$',views.changePassword), 20 | url(r'^changeBirthday$',views.changeBirthday), 21 | url(r'^addMedicalRecord$',views.addMedicalRecord), 22 | url(r'^test$',views.test), 23 | ] 24 | 25 | -------------------------------------------------------------------------------- /api/views.py: -------------------------------------------------------------------------------- 1 | #coding=utf8 2 | from django.shortcuts import render 3 | from django.http import HttpResponse 4 | from models import * 5 | import sys 6 | import json 7 | from django.views.decorators.csrf import csrf_exempt 8 | import hashlib 9 | import random 10 | 11 | 12 | def MD5(s): 13 | '''对字符串s进行md5加密,并返回''' 14 | m = hashlib.md5() 15 | m.update(s) 16 | return m.hexdigest() 17 | 18 | def hello(request): 19 | '''测试接口''' 20 | return HttpResponse("Hello, I am iHealth ' backend!") 21 | 22 | def articleList(request): 23 | '''文章接口''' 24 | # 提取参数 25 | page = int(request.GET.get('page',1)) 26 | limit = int(request.GET.get('limit',10)) 27 | userID = request.GET.get('userID', None) 28 | #cate为首页显示的的文章分类,None时返回所有文章 29 | cate = request.GET.get('cate', None) 30 | 31 | #文章分类为空时: 32 | if cate == None: 33 | # 获取数据 34 | article_list = Articles().find_all() 35 | else: 36 | #文章分类为 推荐 37 | if cate == 'recommend': 38 | #游客,返回所有文章 39 | if userID == None: 40 | article_list = Articles().find_all() 41 | else: 42 | #返回用户label 43 | labels = Users().find_label(userID) 44 | if labels == {} or labels == None: 45 | #用户没有labels 46 | article_list = Articles().find_all() 47 | if labels == None: 48 | #对没有labels的用户设置labels 49 | Users().insert_label(userID) 50 | else: 51 | # 获取对应label的数据 52 | # article_C每一维为一个分类的文章 53 | article_C = Articles().find_recommendArticle(labels) 54 | # num_arc_beg 起始文章标号 num_arc_end 结束文章标号 55 | num_arc_beg = (page - 1) * limit 56 | num_arc_end = page * limit 57 | article_list = [] 58 | len_c = len(article_C) 59 | for i in range(num_arc_beg, num_arc_end): 60 | article_list.append(article_C[i%len_c][i/len_c]) 61 | #一页的文章随机打乱 62 | # random.shuffle(article_list) 63 | #直接返回 64 | res_list = [] 65 | for article in article_list: 66 | # 将对象中不是字符串的变量值转换为字符串 67 | article['_id'] = article['_id'].__str__() 68 | article['pubdate'] = article['pubdate'].__str__() 69 | # article['content'] = article['content'].strip() 70 | article['intro'] = article['intro'].strip() 71 | del article['content'] 72 | res_list.append(article) 73 | # 转换为JSON 74 | # res = json.dumps(res_list, indent=4, ensure_ascii=False, encoding='utf-8') 75 | res = json.dumps(res_list, indent=4) 76 | return HttpResponse(res, content_type='application/json') 77 | else: 78 | article_list = Articles().find_labelArticle(cate) 79 | 80 | # 截取数据 81 | article_list = article_list[(page-1)*limit:(page-1)*limit+limit] 82 | res_list = [] 83 | for article in article_list: 84 | # 将对象中不是字符串的变量值转换为字符串 85 | article['_id'] = article['_id'].__str__() 86 | article['pubdate'] = article['pubdate'].__str__() 87 | # article['content'] = article['content'].strip() 88 | article['intro'] = article['intro'].strip() 89 | del article['content'] 90 | res_list.append(article) 91 | # 转换为JSON 92 | # res = json.dumps(res_list, indent=4, ensure_ascii=False, encoding='utf-8') 93 | res = json.dumps(res_list, indent=4) 94 | return HttpResponse(res, content_type='application/json') 95 | 96 | def articleDetail(request): 97 | '''文章详情接口''' 98 | try: 99 | # 提取参数 100 | id = request.GET.get('id',None) 101 | userID = request.GET.get('userID', None) 102 | 103 | if id == None: 104 | return HttpResponse('请提供 id 参数!') 105 | # 更新文章阅读量 106 | Articles().updateRead(id=id,cnt=1) 107 | # 获取数据 108 | article = Articles().find_one(id=id) 109 | # 更新用户label,个性化推荐用 阅读暂定+1 110 | if userID != None: 111 | Users().update_label(userID, article['category'], 1) 112 | # 准备文章数据,转换为 JSON 113 | del article['_id'] 114 | del article['intro'] 115 | article['pubdate'] = article['pubdate'].__str__() 116 | article['content'] = article['content'].strip() 117 | res = json.dumps(article, indent=4) 118 | return HttpResponse(res, content_type='application/json') 119 | except Exception,e: 120 | res = { 121 | 'msg' : '文章详情获取失败!', 122 | 'reason' : str(e), 123 | } 124 | res = json.dumps(res, indent=4) 125 | return HttpResponse(res, content_type='application/json') 126 | 127 | def doUpvote(request): 128 | '''点赞接口''' 129 | try: 130 | id=request.GET.get('id',None) 131 | userID = request.GET.get('userID', None) 132 | if id == None: 133 | return HttpResponse('请提供 id 参数!') 134 | 135 | Articles().updateUpvote(id=id) 136 | res = { 137 | 'msg' : '点赞成功!', 138 | 'result' : True, 139 | } 140 | article = Articles().find_one(id=id) 141 | # 更新用户label,个性化推荐用 点赞暂定+10 142 | if userID != None: 143 | Users().update_label(userID, article['category'], 10) 144 | except Exception,e: 145 | res = { 146 | 'msg' : '点赞失败!', 147 | 'reason' : str(e), 148 | 'result' : False, 149 | } 150 | res = json.dumps(res, indent=4) 151 | return HttpResponse(res, content_type='application/json') 152 | 153 | # 添加该装饰器以关闭默认post提交的csrf验证 154 | @csrf_exempt 155 | def userCheck(request): 156 | '''检查用户是否可以登录''' 157 | try: 158 | # 获取post提交的数据 159 | user = request.POST 160 | print user 161 | real_user = Users().find_one_by_email(user['email']) 162 | if real_user == None: 163 | res = { 164 | 'msg' : '用户登陆验证未通过!', 165 | 'reason' : 'User is not found.', 166 | 'result' : False, 167 | } 168 | elif user['password'] == real_user['password']: #取消MD5再次加密 169 | real_user['_id'] = str(real_user['_id']) 170 | # del real_user['password'] 171 | res = { 172 | 'msg' : '用户登陆验证通过!', 173 | 'data' : real_user, 174 | 'result' : True, 175 | } 176 | else: 177 | res = { 178 | 'msg' : '用户登陆验证未通过!', 179 | 'reason' : 'Password error.', 180 | 'result' : False, 181 | } 182 | res = json.dumps(res, indent=4) 183 | return HttpResponse(res, content_type='application/json') 184 | except Exception,e: 185 | res = { 186 | 'msg' : '用户登陆验证过程失败!', 187 | 'reason' : str(e), 188 | 'result' : False, 189 | } 190 | res = json.dumps(res, indent=4) 191 | return HttpResponse(res, content_type='application/json') 192 | 193 | 194 | def userList(request): 195 | '''获取用户列表''' 196 | try: 197 | # 提取参数 198 | name = request.GET.get('name','') 199 | selfname = request.GET.get('selfname','') 200 | limit = int(request.GET.get('limit',25)) 201 | # 获取数据 202 | user_list = Users().find_many_by_name(name) 203 | # 截取数据 204 | user_list = user_list[:limit] 205 | res_list = [] 206 | for user in user_list: 207 | # 将对象中不是字符串的变量值转换为字符串 208 | user['_id'] = user['_id'].__str__() 209 | # 排除掉自身 210 | if user['name']==selfname: 211 | continue 212 | del user['password'] 213 | res_list.append(user) 214 | # 转换为JSON 215 | res = json.dumps(res_list, indent=4) 216 | return HttpResponse(res, content_type='application/json') 217 | except Exception,e: 218 | res = { 219 | 'msg' : '模糊匹配失败指定用户名失败!', 220 | 'reason' : str(e), 221 | } 222 | res = json.dumps(res, indent=4) 223 | return HttpResponse(res, content_type='application/json') 224 | 225 | 226 | def userDetail(request): 227 | '''用户详情接口''' 228 | try: 229 | # 提取参数 230 | id = request.GET.get('id',None) 231 | if id == None: 232 | return HttpResponse('请提供 id 参数!') 233 | # 获取数据 234 | user = Users().find_one(id=id) 235 | #出于安全性,将password字段去掉 236 | del user['password'] 237 | # 准备文章数据,转换为 JSON 238 | user['_id'] = str(user['_id']) 239 | res = json.dumps(user, indent=4) 240 | return HttpResponse(res, content_type='application/json') 241 | except Exception,e: 242 | res = { 243 | 'msg' : '用户详情获取失败!', 244 | 'reason' : str(e), 245 | } 246 | res = json.dumps(res, indent=4) 247 | return HttpResponse(res, content_type='application/json') 248 | 249 | @csrf_exempt 250 | def regUser(request): 251 | '''注册用户''' 252 | try: 253 | data = request.POST.copy() 254 | print data 255 | # 判断是否已存在该邮箱注册的账号 256 | if not (data.has_key('email') and data.has_key('password') and data.has_key('nickname') and data.has_key('sex') and data.has_key('usertype') and data.has_key('birthday')): 257 | raise Exception,'注册信息参数不完整' 258 | if Users().find_one_by_email(data['email']) != None: 259 | raise Exception,'邮箱已被注册过' 260 | # 插入数据 261 | #将下面一行注释掉,取消MD5再次加密 262 | #data['password'] = MD5(data['password']) 263 | #设初始labels为空,个性化推荐用 264 | data['labels'] = {} 265 | 266 | #初始化用户病历字段 267 | data['medicalRecord']=[] 268 | data['age'] = int(data['age']) 269 | data['sex'] = int(data['sex']) 270 | data['usertype'] = int(data['usertype']) 271 | 272 | Users().insert_one(data) 273 | res = { 274 | 'msg' : '用户注册成功!', 275 | 'result' : True, 276 | } 277 | except Exception,e: 278 | res = { 279 | 'msg' : '用户注册失败!', 280 | 'reason' : str(e), 281 | 'result' : False, 282 | } 283 | res = json.dumps(res, indent=4) 284 | return HttpResponse(res, content_type='application/json') 285 | 286 | def test(request): 287 | return render(request,'index.html') 288 | 289 | 290 | #根据id,进行密码验证 291 | def check_password(id,password): 292 | realPwd = Users().get_password_by_id(id) 293 | if password == realPwd: 294 | return True 295 | else : 296 | return False 297 | 298 | #根据id列表取出users用于群聊 299 | def userListByID(request): 300 | try: 301 | usersId=request.GET.get('id',None) 302 | if usersId == None: 303 | raise Exception,'请提供 id 参数' 304 | 305 | usersID=usersId.split(',') 306 | 307 | users=[] 308 | for ID in usersID: 309 | temp=Users().find_one(id=ID) 310 | temp['_id']=temp['_id'].__str__() 311 | users.append(temp) 312 | res = { 313 | 'msg' : '获取成功!', 314 | 'data' : users, 315 | 'result' : True, 316 | } 317 | except Exception,e: 318 | res = { 319 | 'msg' : '获取失败!', 320 | 'reason' : str(e), 321 | 'result' : False, 322 | } 323 | res = json.dumps(res, indent=4) 324 | return HttpResponse(res, content_type='application/json') 325 | 326 | #修改昵称 327 | @csrf_exempt 328 | def changeNickname(request): 329 | try: 330 | data = request.POST.copy() 331 | if not(data.has_key('id') and data.has_key('password') and data.has_key('newName')): 332 | raise Exception,'注册信息参数不完整' 333 | userID = data['id'] 334 | password = data['password'] 335 | name = data['newName'] 336 | if check_password(userID,password) == False: 337 | res = { 338 | 'msg' : '修改失败,请保证网络安全', 339 | 'result' : False, 340 | } 341 | else : 342 | Users().changeNickname(userID,name) 343 | res = { 344 | 'msg' : '修改成功', 345 | 'result' : True, 346 | } 347 | except Exception,e: 348 | res={ 349 | 'msg' : '修改失败', 350 | 'reason' : str(e), 351 | 'result' : False, 352 | } 353 | res = json.dumps(res,indent=4) 354 | return HttpResponse(res, content_type='application/json') 355 | 356 | #修改手机号 357 | @csrf_exempt 358 | def changePhone(request): 359 | try: 360 | data = request.POST.copy() 361 | if not(data.has_key('id') and data.has_key('password') and data.has_key('newPhone')): 362 | raise Exception,'注册信息参数不完整' 363 | userID = data['id'] 364 | password = data['password'] 365 | phone = data['newPhone'] 366 | if check_password(userID,password) == False: 367 | res = { 368 | 'msg' : '修改失败,请保证网络安全', 369 | 'result' : False, 370 | } 371 | else : 372 | Users().changePhone(userID,phone) 373 | res = { 374 | 'msg' : '修改成功', 375 | 'result' : True, 376 | } 377 | except Exception,e: 378 | res={ 379 | 'msg' : '修改失败', 380 | 'reason' : str(e), 381 | 'result' : False, 382 | } 383 | res = json.dumps(res,indent=4) 384 | return HttpResponse(res, content_type='application/json') 385 | 386 | #修改姓名 387 | @csrf_exempt 388 | def changeName(request): 389 | try: 390 | data = request.POST.copy() 391 | if not(data.has_key('id') and data.has_key('password') and data.has_key('newName')): 392 | raise Exception,'注册信息参数不完整' 393 | userID = data['id'] 394 | password = data['password'] 395 | name = data['newName'] 396 | 397 | if check_password(userID,password) == False: 398 | res = { 399 | 'msg' : '修改失败,请保证网络安全', 400 | 'result' : False, 401 | } 402 | else : 403 | Users().changeName(userID,name) 404 | res = { 405 | 'msg' : '修改成功', 406 | 'result' : True, 407 | } 408 | except Exception,e: 409 | res={ 410 | 'msg' : '修改失败', 411 | 'reason' : str(e), 412 | 'result' : False, 413 | } 414 | res = json.dumps(res,indent=4) 415 | return HttpResponse(res, content_type='application/json') 416 | 417 | 418 | #修改性别 419 | @csrf_exempt 420 | def changeSex(request): 421 | try: 422 | data = request.POST.copy() 423 | if not(data.has_key('id') and data.has_key('password') and data.has_key('newSex')): 424 | raise Exception,'注册信息参数不完整' 425 | userID = data['id'] 426 | password = data['password'] 427 | sex = data['newSex'] 428 | 429 | if check_password(userID,password) == False: 430 | res = { 431 | 'msg' : '修改失败,请保证网络安全', 432 | 'result' : False, 433 | } 434 | else : 435 | Users().changeSex(userID,sex) 436 | res = { 437 | 'msg' : '修改成功', 438 | 'result' : True, 439 | } 440 | except Exception,e: 441 | res={ 442 | 'msg' : '修改失败', 443 | 'reason' : str(e), 444 | 'result' : False, 445 | } 446 | res = json.dumps(res,indent=4) 447 | return HttpResponse(res, content_type='application/json') 448 | 449 | #修改密码,需要同时输入原密码和新密码 450 | @csrf_exempt 451 | def changePassword(request): 452 | try: 453 | data = request.POST.copy() 454 | if not(data.has_key('id') and data.has_key('oldPassword') and data.has_key('newPassword')): 455 | raise Exception,'注册信息参数不完整' 456 | userID = data['id'] 457 | oldPwd = data['oldPassword'] 458 | newPwd = data['newPassword'] 459 | 460 | if check_password(userID,oldPwd) == False: #取消MD5再次加密 461 | res = { 462 | 'msg' : '密码错误,修改失败', 463 | 'result' : False, 464 | } 465 | else : 466 | Users().changePassword(userID,newPwd) #取消MD5再次加密 467 | res = { 468 | 'msg' : '修改成功', 469 | 'result' : True, 470 | } 471 | except Exception,e: 472 | res={ 473 | 'msg' : '修改失败', 474 | 'reason' : str(e), 475 | 'result' : False, 476 | } 477 | res = json.dumps(res,indent=4) 478 | return HttpResponse(res, content_type='application/json') 479 | 480 | #修改出生日期 481 | @csrf_exempt 482 | def changeBirthday(request): 483 | try: 484 | data = request.POST.copy() 485 | if not(data.has_key('id') and data.has_key('password') and data.has_key('newBirthday')): 486 | raise Exception,'注册信息参数不完整' 487 | print data 488 | userID = data['id'] 489 | password = data['password'] 490 | Date = data['newBirthday'] 491 | 492 | if check_password(userID,password) == False: 493 | res = { 494 | 'msg' : '修改失败,请保证网络安全', 495 | 'result' : False, 496 | } 497 | else : 498 | Users().changeBirthday(userID,Date) 499 | res = { 500 | 'msg' : '修改成功', 501 | 'result' : True, 502 | } 503 | except Exception,e: 504 | res={ 505 | 'msg' : '修改失败', 506 | 'reason' : str(e), 507 | 'result' : False, 508 | } 509 | res = json.dumps(res,indent=4) 510 | return HttpResponse(res, content_type='application/json') 511 | 512 | #写入病历 513 | @csrf_exempt 514 | def addMedicalRecord(request): 515 | try: 516 | data = request.POST.copy() 517 | if not(data.has_key('id') and data.has_key('date') and data.has_key('doctor') and data.has_key('content')): 518 | raise Exception,'注册信息参数不完整' 519 | print data 520 | userID = data['id'] 521 | 522 | toData = {} 523 | toData['date']=data['date'] 524 | toData['doctor']=data['doctor'] 525 | toData['content']=data['content'] 526 | Users().addMedicalRecord(userID,toData) 527 | res = { 528 | 'msg' : '修改成功', 529 | 'result' : True, 530 | } 531 | except Exception,e: 532 | res={ 533 | 'msg' : '修改失败', 534 | 'reason' : str(e), 535 | 'result' : False, 536 | } 537 | res = json.dumps(res,indent=4) 538 | return HttpResponse(res, content_type='application/json') 539 | -------------------------------------------------------------------------------- /iHealth_site/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iHealth-ecnu/iHealth_site/59cbdac8b574e8c97b47896ebb8a56f22e0af33e/iHealth_site/__init__.py -------------------------------------------------------------------------------- /iHealth_site/settings.py: -------------------------------------------------------------------------------- 1 | #coding=utf8 2 | """ 3 | Django settings for iHealth_site project. 4 | 5 | Generated by 'django-admin startproject' using Django 1.8.17. 6 | 7 | For more information on this file, see 8 | https://docs.djangoproject.com/en/1.8/topics/settings/ 9 | 10 | For the full list of settings and their values, see 11 | https://docs.djangoproject.com/en/1.8/ref/settings/ 12 | """ 13 | 14 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 15 | import os 16 | 17 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 18 | 19 | 20 | # Quick-start development settings - unsuitable for production 21 | # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ 22 | 23 | # SECURITY WARNING: keep the secret key used in production secret! 24 | SECRET_KEY = 'o3b&e$1gr$r(p3_n^7oucceox-sh2r21wyfmxv2sorw+b!z8gu' 25 | 26 | # SECURITY WARNING: don't run with debug turned on in production! 27 | DEBUG = True 28 | 29 | ALLOWED_HOSTS = ['*'] 30 | 31 | 32 | # Application definition 33 | 34 | INSTALLED_APPS = ( 35 | 'django.contrib.admin', 36 | 'django.contrib.auth', 37 | 'django.contrib.contenttypes', 38 | 'django.contrib.sessions', 39 | 'django.contrib.messages', 40 | 'django.contrib.staticfiles', 41 | 'api', 42 | ) 43 | 44 | MIDDLEWARE_CLASSES = ( 45 | 'django.contrib.sessions.middleware.SessionMiddleware', 46 | 'django.middleware.common.CommonMiddleware', 47 | 'django.middleware.csrf.CsrfViewMiddleware', 48 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 49 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 50 | 'django.contrib.messages.middleware.MessageMiddleware', 51 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 52 | 'django.middleware.security.SecurityMiddleware', 53 | ) 54 | 55 | ROOT_URLCONF = 'iHealth_site.urls' 56 | 57 | TEMPLATES = [ 58 | { 59 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 60 | 'DIRS': [], 61 | 'APP_DIRS': True, 62 | 'OPTIONS': { 63 | 'context_processors': [ 64 | 'django.template.context_processors.debug', 65 | 'django.template.context_processors.request', 66 | 'django.contrib.auth.context_processors.auth', 67 | 'django.contrib.messages.context_processors.messages', 68 | ], 69 | }, 70 | }, 71 | ] 72 | 73 | WSGI_APPLICATION = 'iHealth_site.wsgi.application' 74 | 75 | 76 | # Database 77 | # https://docs.djangoproject.com/en/1.8/ref/settings/#databases 78 | 79 | DATABASES = { 80 | 'default': { 81 | 'ENGINE': 'django.db.backends.sqlite3', 82 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 83 | } 84 | } 85 | 86 | 87 | # Internationalization 88 | # https://docs.djangoproject.com/en/1.8/topics/i18n/ 89 | 90 | LANGUAGE_CODE = 'en-us' 91 | 92 | TIME_ZONE = 'UTC' 93 | 94 | USE_I18N = True 95 | 96 | USE_L10N = True 97 | 98 | USE_TZ = True 99 | 100 | 101 | # Static files (CSS, JavaScript, Images) 102 | # https://docs.djangoproject.com/en/1.8/howto/static-files/ 103 | 104 | STATIC_URL = '/static/' 105 | 106 | 107 | # 自定义设置 108 | MONGO_HOST = '39.106.10.31' 109 | MONGO_PORT = 27017 110 | MONGO_DBNAME = 'iHealth' 111 | MONGO_AUTHDB = 'iHealth' 112 | MONGO_USER = 'admin' 113 | MONGO_PWD = 'admin123' 114 | -------------------------------------------------------------------------------- /iHealth_site/urls.py: -------------------------------------------------------------------------------- 1 | """iHealth_site URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.8/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) 14 | """ 15 | from django.conf.urls import include, url 16 | from django.contrib import admin 17 | 18 | from api import views 19 | 20 | urlpatterns = [ 21 | url(r'^$', views.hello), 22 | url(r'^admin/', include(admin.site.urls)), 23 | url(r'^api/v1/', include('api.urls')), 24 | ] 25 | -------------------------------------------------------------------------------- /iHealth_site/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for iHealth_site project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "iHealth_site.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /log/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iHealth-ecnu/iHealth_site/59cbdac8b574e8c97b47896ebb8a56f22e0af33e/log/.gitkeep -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "iHealth_site.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /uwsgi.ini: -------------------------------------------------------------------------------- 1 | #Django-related settings 2 | [uwsgi] 3 | 4 | socket =:8002 5 | 6 | # the base directory (full path) 7 | chdir = /home/yym/workspace/iHealth_site 8 | 9 | # Django s wsgi file 10 | module = iHealth_site.wsgi 11 | 12 | # process-related settings 13 | # master 14 | master = true 15 | 16 | # maximum number of worder processs 17 | processes = 4 18 | 19 | # ...with appropriate permissions - may be needed 20 | # chmod-socket = 664 21 | # clear enviroment on exit 22 | vacuum = true 23 | 24 | buffer-size = 32768 25 | 26 | # off logging , because it can record some request info. This is inefficiency. 27 | #disable-logging = true 28 | 29 | # exceed specified memory to reload this uwsgi thread. 30 | # as - virtual mem 31 | # rss - physical mem 32 | # evil-reload-on-as = 100 33 | evil-reload-on-rss = 100 34 | -------------------------------------------------------------------------------- /uwsgiserver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ ! -n "$1" ] 3 | then 4 | echo "Usages: sh uwsgiserver.sh [start|stop|restart|status|log]" 5 | exit 0 6 | fi 7 | 8 | if [ $1 = start ] 9 | then 10 | psid=`ps aux | grep "uwsgi" | grep "iHealth_site" | grep -v "grep" | wc -l` 11 | if [ $psid -gt 5 ] 12 | then 13 | echo "iHealth_site'uwsgi is running!" 14 | exit 0 15 | else 16 | uwsgi --ini uwsgi.ini --daemonize log/uwsgi.log --module iHealth_site.wsgi 17 | echo "Start iHealth_site'uwsgi service [OK]" 18 | fi 19 | 20 | 21 | elif [ $1 = stop ];then 22 | ps -ef | grep "iHealth_site" | grep -v grep | cut -c 10-15 | xargs kill -9 23 | echo "Stop iHealth_site'uwsgi service [OK]" 24 | 25 | elif [ $1 = restart ];then 26 | ps -ef | grep "iHealth_site" | grep -v grep | cut -c 10-15 | xargs kill -9 27 | echo "Stop iHealth_site'uwsgi service [OK]" 28 | sleep 2 29 | uwsgi --ini uwsgi.ini --daemonize log/uwsgi.log --module iHealth_site.wsgi 30 | echo "Start iHealth_site'uwsgi service [OK]" 31 | 32 | elif [ $1 = status ];then 33 | ps -ef | grep "iHealth_site" | grep -v grep 34 | 35 | elif [ $1 = log ];then 36 | tail -f log/uwsgi.log 37 | 38 | else 39 | echo "Usages: sh uwsgiserver.sh [start|stop|restart|status|log]" 40 | fi 41 | --------------------------------------------------------------------------------