From c46805e77bdb49f62044cff883789a478754a20c Mon Sep 17 00:00:00 2001 From: Flinner Yuu Date: Wed, 4 Dec 2024 11:51:16 +0300 Subject: [PATCH] feat: init --- CMakeLists.txt | 24 ++++++ Clinic.cpp | 172 +++++++++++++++++++++++++++++++++++++ Patient.cpp | 111 ++++++++++++++++++++++++ Patient.hpp | 53 ++++++++++++ Visit.cpp | 23 +++++ Visit.hpp | 35 ++++++++ linkedList.cpp | 201 ++++++++++++++++++++++++++++++++++++++++++++ main.cpp | 73 ++++++++++++++++ patient_files/1.txt | 9 ++ patient_files/6.txt | 6 ++ patients.txt | 3 + 11 files changed, 710 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 Clinic.cpp create mode 100644 Patient.cpp create mode 100644 Patient.hpp create mode 100644 Visit.cpp create mode 100644 Visit.hpp create mode 100644 linkedList.cpp create mode 100644 main.cpp create mode 100644 patient_files/1.txt create mode 100644 patient_files/6.txt create mode 100644 patients.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b4c72df --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,24 @@ +# Minimum required version of CMake +cmake_minimum_required(VERSION 3.10) + +# Project name +project(ClinicProject) + +# Set the C++ standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Add the executable target for the project +add_executable(ClinicApp + main.cpp + Clinic.cpp + Patient.cpp + Visit.cpp + +) + +# Include directories if necessary (e.g., for .hpp files) +include_directories(${CMAKE_SOURCE_DIR}) + +# If you have any custom configurations or testing requirements, +# you can define them here, such as linking libraries or adding flags. diff --git a/Clinic.cpp b/Clinic.cpp new file mode 100644 index 0000000..a270e6d --- /dev/null +++ b/Clinic.cpp @@ -0,0 +1,172 @@ +#include "Patient.hpp" +#include +#include +#include +#include +#include +#include +#include + +#ifndef CLINIC_ +#define CLINIC_ + +enum class MenuChoice { + NewPatient = 1, + NewVisit = 2, + DisplayPatient = 3, + Quit = 4, +}; + +struct Clinic { +public: + std::vector patients; + std::string filename; + +private: + // C++ types are getting crazy here :( I really miss Rust + // `std::optional` needed because a patient might not be found + // `std::reference_wrapper` because `std::optional` deoesn't accept references + // I want to reference because (I want to save cpu cycles from memory copies) + std::optional> + find_patient(u_int32_t patient_id) { + auto it = std::find_if(patients.begin(), patients.end(), + [&id = patient_id](const Patient &p) -> bool { + return p.get_id() == id; + }); + if (it != patients.end()) + return *it; + return std::nullopt; + }; + +public: + Clinic(std::string filename) : filename(filename) { + /// this file contains a list of patient IDs. + // 1 + // 2 + // 4 (could be non-sequential + // but always sorted!) + u_int32_t patient_id; + std::ifstream file{filename}; + if (!file.is_open()) { + std::cerr << "File " << filename << " couldn't be opened :(\n"; + exit(1); + } + + while (file >> patient_id) + patients.push_back(Patient(patient_id)); + } + + // writes the patients.txt file containing patient IDs. + void write_file() { + std::ofstream file{filename}; + for (auto p : patients) + file << p.get_id() << "\n"; + } + u_int32_t generate_id() { + u_int32_t id{}; + // O(n), could be improved by using UUIDs, or store max_id in Clinic + for (auto p : patients) + id = std::max(id, p.get_id()); + id++; + return id; + } + + MenuChoice menu_main() { + int choice{}; + std::cout << "==============================================\n"; + std::cout << "Please choose a command:\n"; + std::cout << "1. add new patient\n"; + std::cout << "2. add new visit\n"; + std::cout << "3. display patient information\n"; + std::cout << "4. quit\n"; + std::cout << ">> "; + + std::cin >> choice; + + while (choice < int(MenuChoice::NewPatient) || + choice > int(MenuChoice::Quit)) { + std::cout << "Invalid input! Try again :)\n"; + std::cout << "Please choose a command:\n"; + std::cout << "1. add new patient\n"; + std::cout << "2. add new visit\n"; + std::cout << "3. display patient information\n"; + std::cout << "4. quit\n"; + std::cout << ">> "; + std::cin >> choice; + } + return static_cast(choice); + } + + void menu_patient_info() { + int patient_id; + std::cout << "Patient file number: "; + std::cin >> patient_id; + auto patient = find_patient(patient_id); + if (patient.has_value()) + std::cout << patient.value(); + else + std::cout << "No Patient exists with id= " << patient_id << "\n"; + std::cout << "--------------------\n\n"; + } + + void menu_add_new_patient() { + std::string reading_string; + std::string input_string; + Patient patient{}; + std::stringstream patient_stream; + + // not the most optimal way, but the lazy way + // see: https://ammar.engineer/posts/2023/06/03/laziness/ + // this "emulates" a file so that I can resuse my >> operator + reading_string += std::to_string(generate_id()) + "\n"; + std::cout << "Generate " << reading_string; + std::cout << "First name (no spaces): "; + std::cin >> input_string; + reading_string += input_string + "\n"; + std::cout << "Last name (no spaces): "; + std::cin >> input_string; + reading_string += input_string + "\n"; + std::cout << "Gender (M or F): "; + std::cin >> input_string; + reading_string += input_string + "\n"; + std::cout << "Age: "; + std::cin >> input_string; + reading_string += input_string + "\n"; + std::cout << reading_string; + patient_stream = std::stringstream(reading_string); + + // all of that just for this moment: + patient_stream >> patient; + patients.push_back(patient); // save in memory + patient.write_patient(); // write to file + write_file(); + } + void menu_add_vist() { + std::string reading_string; + std::string input_string; + int patient_id; + + std::cout << "Patient file number: "; + std::cin >> patient_id; + std::cout << "Date:"; + std::cin >> input_string; + reading_string += input_string + " "; + std::cout << "Doctor's First Name: "; + std::cin >> input_string; + reading_string += input_string + " "; + std::cout << "Doctors's Last Name: "; + std::cin >> input_string; + reading_string += input_string + " "; + + std::stringstream visit_stream = std::stringstream(reading_string); + Visit visit; + visit_stream >> visit; + auto patient = find_patient(patient_id); + if (patient.has_value()) + patient.value().get().add_visit(visit); + else + std::cout << "No Patient exists with id= " << patient_id << "\n"; + } +}; + +#endif diff --git a/Patient.cpp b/Patient.cpp new file mode 100644 index 0000000..5ae863a --- /dev/null +++ b/Patient.cpp @@ -0,0 +1,111 @@ +// The class Patient stores the personal information of a patient as well as +// his/her history of medical visits as a vector of Visit objects, all as +// private data members. The class interface allows user code to: + +// - Instantiate a Patient object given (as parameters) all his/her +// personal information. No visit history is required. The same +// function should also create a patient record file according to +// the format specified above. + +// - Instantiate a Patient object given (as parameter) his/her ID +// number. The personal information as well as the visit history of +// the patients to be read from his existing record file. + +// - Add one Visit record given a Visit object as parameter. The same +// function should record the Visit in the patient file. + +// - Print the patient record according to the format specified in +// the files above using the << operator (including history) + +#include "Patient.hpp" +#include "Visit.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////// +// File: patient_files/3.txt // +// 3 // +// Amirah // +// Asaad // +// F // +// 26 // +// 3 1/1/2018 Abdullah Alshareef // +// 9 4/1/2018 Helen Philips // +// 14 8/1/2018 Abdullah Alshareef // +//////////////////////////////////// + +Patient::Patient(u_int32_t id) : id(id) { + auto patient_file_path = "patient_files/" + std::to_string(id) + ".txt"; + std::ifstream patient_file{patient_file_path}; + if (patient_file.is_open()) { + patient_file >> *this; + } else { + std::cerr << "Failed to read patient file at: " + patient_file_path; + exit(1); + } +} + +void Patient::write_patient() { + // create patient's file + // this will overwrite any patient with the same id + auto patient_file_path = "patient_files/" + std::to_string(id) + ".txt"; + std::ofstream patient_file{patient_file_path}; + if (patient_file.is_open()) { + patient_file << *this; + } else { + std::cerr << "Failed to create patient file at: " + patient_file_path; + exit(1); + } +} + +u_int32_t Patient::get_id() const { return id; } + +void Patient::add_visit(Visit const &visit) { + visits.push_back(visit); + // could have logic to append data and not re-write the whole thing, but not + // worth it + // this rewrites the entire file each time a visits gets added + write_patient(); +} + +std::ostream &operator<<(std::ostream &out, Patient &patient) { + // clang-format off + out << patient.id << "\n"; + out << patient.first_name << "\n"<< patient.last_name << "\n"; + out << patient.gender_char() << "\n"; + out << patient.age << "\n"; + for (auto visit : patient.visits) + out << visit << "\n"; + return out; + // clang-format on +} +std::istream &operator>>(std::istream &in, Patient &patient) { + char gender_char{}; + in >> patient.id; + in >> patient.first_name >> patient.last_name; + in >> gender_char; + in >> patient.age; + + patient.gender = gender_char == 'M' ? true : false; + + std::string visit_string; + Visit visit{}; + + // read to string, convert to string stream, read from that stream again + // weird C++ gymnastics (or bad design by me) + getline(in, visit_string); // first string is always empty for some reason + while (getline(in, visit_string)) { + std::stringstream visit_stream(visit_string); + visit_stream >> visit; + patient.visits.push_back(visit); + } + + return in; + // clang-format on +} diff --git a/Patient.hpp b/Patient.hpp new file mode 100644 index 0000000..3708b95 --- /dev/null +++ b/Patient.hpp @@ -0,0 +1,53 @@ +#ifndef PATIENT_HPP +#define PATIENT_HPP + +#include "Visit.hpp" +#include +#include +#include +#include +#include +#include + +//////////////////////////////////// +// File: patient_files/3.txt // +// 3 // +// Amirah // +// Asaad // +// F // +// 26 // +// 3 1/1/2018 Abdullah Alshareef // +// 9 4/1/2018 Helen Philips // +// 14 8/1/2018 Abdullah Alshareef // +//////////////////////////////////// + +class Patient { +private: + // clang-format off + u_int32_t id; + std::string first_name; + std::string last_name; + bool gender; // true gender = M, false gender = F + u_int32_t age; + std::vector visits; + + char gender_char() { return gender ? 'M' : 'F'; } + +public: + void write_patient(); + Patient(){}; + Patient(u_int32_t id, std::string first_name, std::string last_name, bool gender, u_int age) + : id(id), first_name(first_name), last_name(last_name), gender(gender), age(age) { + // clang-format on + write_patient(); + } + Patient(u_int32_t id); + u_int32_t get_id() const; + +public: + void add_visit(Visit const &visit); + friend std::ostream &operator<<(std::ostream &out, Patient &patient); + friend std::istream &operator>>(std::istream &in, Patient &patient); +}; + +#endif diff --git a/Visit.cpp b/Visit.cpp new file mode 100644 index 0000000..0f7bdca --- /dev/null +++ b/Visit.cpp @@ -0,0 +1,23 @@ +#include +#include +#include +#include + +#include "Visit.hpp" + +std::istream &operator>>(std::istream &in, Visit &visit) { + in >> visit.visit_id; + in >> visit.date; + in >> visit.doctor_first_name; + in >> visit.doctor_last_name; + return in; +} + +std::ostream &operator<<(std::ostream &out, Visit &visit) { + auto v = visit; + out << v.visit_id << " " // + << v.date << " " // + << v.doctor_first_name << " " // + << v.doctor_last_name; + return out; +} diff --git a/Visit.hpp b/Visit.hpp new file mode 100644 index 0000000..531615a --- /dev/null +++ b/Visit.hpp @@ -0,0 +1,35 @@ +#ifndef VISIT_HPP +#define VISIT_HPP + +#include +#include +#include +#include +#include + +class Visit { + // clang-format off +private: + u_int32_t visit_id{}; + u_int32_t patient_id{};//.txt + std::string date{}; //"dd/mm/yyyy" + std::string doctor_first_name{}; + std::string doctor_last_name{}; + + // clang-format on +public: + Visit() {} + Visit(u_int32_t visit_id, u_int32_t patient_id, std::string date, + std::string doctor_first_name, std::string doctor_last_name) + : visit_id(visit_id), patient_id(patient_id), date(date), + doctor_first_name(doctor_first_name), + doctor_last_name(doctor_last_name) {} + // clang-format off + // clang-format on + + friend std::istream &operator>>(std::istream &in, Visit &visit); + + friend std::ostream &operator<<(std::ostream &out, Visit &visit); +}; + +#endif diff --git a/linkedList.cpp b/linkedList.cpp new file mode 100644 index 0000000..2829d12 --- /dev/null +++ b/linkedList.cpp @@ -0,0 +1,201 @@ +#include +#include +#ifndef LINKED_LIST +#define LINKED_LIST + +using namespace std; + +struct node { + int data; + node *next; + node *previous; +}; + +class List { + // private: +public: + node *head{}; + node *tail{}; + +public: + // A default constructor, which creates an empty list + List() {} + // Not specified in the HW, but needed in my opinion + List(node *head, node *tail) : head(head), tail(tail) {} + // A parametrized constructor, which creates a list out of the elements of an + // array, keeping them in the same given order. + List(char *data, size_t n) { + for (int i{0}; i < n; i++) + appendList(data[i]); + } + // A copy constructor. + List(List const &list) { + auto h = list.head; + while (h != list.tail->next) { + appendList(h->data); + h = h->next; + } + } + + bool isEmptyList(); + std::ostream &printList(std::ostream &out); + void prependList(int); + void appendList(int); + bool findInList(int); + void pop(); + void popBack(); + void removeFromList(node *); + void removeFromList(int); + + friend List operator+(int const a, List const list) { + // copy + List newlist(list); + newlist.prependList(a); + return newlist; + } + friend List operator+(List const list, int a) { + // copy + List newlist(list); + newlist.appendList(a); + return newlist; + } + friend List operator+(List const list1, List const list2) { + // copies + List newlist1(list1); + node *h = list2.head; + while (h != list2.tail->next) { + newlist1.appendList(h->data); + h = h->next; + } + + return newlist1; + } + + node operator[](int i) { + auto h = head; + for (int j{0}; j < i; j++) + h = h->next; + return *h; + } +}; +std::ostream &operator<<(std::ostream &out, List list) { + return list.printList(out); +} + +bool List::isEmptyList() { + if (head == nullptr && tail == nullptr) + return true; + return false; +} +void List::appendList(int data) { + node *node1 = new node{data, nullptr, tail}; + if (head == nullptr && tail == nullptr) { + head = node1; + tail = node1; + } else { + tail->next = node1; + tail = node1; + } +} + +std::ostream &List::printList(std::ostream &out) { + if (isEmptyList()) { + out << "List is empty" << endl; + return out; + } + node *traveller = head; + while (traveller != tail) { + out << traveller->data << "=>"; + traveller = traveller->next; + } + out << tail->data << endl; + return out; +} + +void List::prependList(int data) { + node *node1 = new node{data, head, nullptr}; + if (head == nullptr && tail == nullptr) { + head = node1; + tail = node1; + } else { + head->previous = node1; + head = node1; + } +} + +bool List::findInList(int key) { + // returns a pointer to the first occurrence of the key in the list + node *traveller = head; + while (traveller != tail->next) { + if (traveller->data == key) { + // return traveller; + return true; + } + traveller = traveller->next; + } + return false; +} +void List::pop() { + if (isEmptyList()) + return; + if (head == tail) // one node + { + delete head; + head = nullptr; + tail = nullptr; + return; + } + node *firstNode = head; + head = head->next; + head->previous = nullptr; + delete firstNode; +} + +void List::popBack() { + if (isEmptyList()) + return; + if (head == tail) // one node + { + delete head; + head = nullptr; + tail = nullptr; + return; + } + node *lastNode = tail; + tail = tail->previous; + tail->next = nullptr; + delete lastNode; +} + +void List::removeFromList(node *node1) { + if (node1 == head) { + pop(); + return; + } + if (node1 == tail) { + popBack(); + return; + } + if (node1) { + node *tempPrev = node1->previous; + node *tempNext = node1->next; + tempPrev->next = tempNext; + tempNext->previous = tempPrev; + delete node1; + } +} + +void List::removeFromList(int key) { + node *traveller = head; + while (traveller != tail->next) { + if (traveller->data == key) { + removeFromList(traveller); + return; + } + traveller = traveller->next; + } + + // note that this deletes only the first occurrence of the key in the list +} + +#endif diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..46451bf --- /dev/null +++ b/main.cpp @@ -0,0 +1,73 @@ +#include "Clinic.cpp" +#include "Patient.hpp" +#include "linkedList.cpp" +#include +#include +#include +#include +#include +#include +#include + +int main() { + // this program implements the sieve of Erathosthenes using linked lists + // it does not use/test all the list functions + List primes{nullptr, nullptr}; + for (int i = 2; i <= 20; i++) { + primes.appendList(i); + } + node *p = primes.head; + while (p != primes.tail) { + int prime = p->data; + int tailData = primes.tail->data; + for (int composite = 2 * prime; composite <= tailData; composite += prime) { + primes.removeFromList( + composite); // if the composite is not in the list, nothing happens + } + p = p->next; + } + std::cout << primes; + + // testing copy + List primes_copy(primes); + std::cout << "primes: " << primes; + std::cout << "primes: " << primes_copy + 1; + std::cout << "primes+primes: " << primes_copy + primes_copy; + + // should return 5 + std::cout << "primes[2] = "; + std::cout << primes_copy[2].data << std::endl; + + return 0; +} + +// int main() { +// Clinic clinic("patients.txt"); +// std::cout << "Welcome to patient management\n\n"; + +// // run util main_menu recieves a non-exit value +// MenuChoice choice; +// Patient patient{}; +// std::string reading_string; +// std::string input_string; + +// while (1) { +// choice = clinic.menu_main(); +// switch (choice) { +// case MenuChoice::NewPatient: +// clinic.menu_add_new_patient(); +// break; +// case MenuChoice::NewVisit: +// clinic.menu_add_vist(); +// break; +// case MenuChoice::DisplayPatient: +// clinic.menu_patient_info(); +// break; +// case MenuChoice::Quit: +// return 0; +// break; +// } +// } + +// return 0; +// } diff --git a/patient_files/1.txt b/patient_files/1.txt new file mode 100644 index 0000000..4b8790c --- /dev/null +++ b/patient_files/1.txt @@ -0,0 +1,9 @@ +1 +Joe +Doe +M +18 +1 11/12/103 Mike WWW +1 11/12/103 Mike WWW +1 11/12/103 Mike WWW +77 /88/9999 Doctr Last diff --git a/patient_files/6.txt b/patient_files/6.txt new file mode 100644 index 0000000..5bcc488 --- /dev/null +++ b/patient_files/6.txt @@ -0,0 +1,6 @@ +6 +Karim +Alaswad +M +14 +16 12/1/2018 Helen Philips diff --git a/patients.txt b/patients.txt new file mode 100644 index 0000000..9364ec4 --- /dev/null +++ b/patients.txt @@ -0,0 +1,3 @@ +1 +3 +4